Integrate dialog export and Apple service teams
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
|
||||
#include "foundation/result.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
|
||||
@@ -34,6 +35,41 @@ struct AppInputDialogPlan {
|
||||
std::string ok_caption = "Ok";
|
||||
};
|
||||
|
||||
class AppDialog {
|
||||
public:
|
||||
virtual ~AppDialog() = default;
|
||||
[[nodiscard]] virtual AppDialogKind kind() const noexcept = 0;
|
||||
};
|
||||
|
||||
class AppProgressDialog : public AppDialog {
|
||||
public:
|
||||
~AppProgressDialog() override = default;
|
||||
};
|
||||
|
||||
class AppMessageDialog : public AppDialog {
|
||||
public:
|
||||
~AppMessageDialog() override = default;
|
||||
};
|
||||
|
||||
class AppInputDialog : public AppDialog {
|
||||
public:
|
||||
~AppInputDialog() override = default;
|
||||
};
|
||||
|
||||
class AppDialogFactory {
|
||||
public:
|
||||
virtual ~AppDialogFactory() = default;
|
||||
|
||||
[[nodiscard]] virtual std::shared_ptr<AppProgressDialog> show_progress_dialog(
|
||||
const AppProgressDialogPlan& plan) = 0;
|
||||
|
||||
[[nodiscard]] virtual std::shared_ptr<AppMessageDialog> show_message_dialog(
|
||||
const AppMessageDialogPlan& plan) = 0;
|
||||
|
||||
[[nodiscard]] virtual std::shared_ptr<AppInputDialog> show_input_dialog(
|
||||
const AppInputDialogPlan& plan) = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] inline AppProgressDialogPlan plan_app_progress_dialog(
|
||||
std::string_view title,
|
||||
int total) noexcept
|
||||
|
||||
@@ -492,9 +492,9 @@ public:
|
||||
case DocumentExportExecutionKind::layers_stem:
|
||||
case DocumentExportExecutionKind::animation_frames_collection:
|
||||
case DocumentExportExecutionKind::animation_frames_stem:
|
||||
case DocumentExportExecutionKind::depth:
|
||||
case DocumentExportExecutionKind::cube_faces:
|
||||
return true;
|
||||
case DocumentExportExecutionKind::depth:
|
||||
case DocumentExportExecutionKind::animation_mp4:
|
||||
case DocumentExportExecutionKind::timelapse:
|
||||
return false;
|
||||
|
||||
@@ -126,13 +126,15 @@ void start_document_export_collection(
|
||||
std::shared_ptr<NodeProgressBar> App::show_progress(const std::string& title, int total /*= 0*/)
|
||||
{
|
||||
const auto plan = pp::app::plan_app_progress_dialog(title, total);
|
||||
return pp::panopainter::create_legacy_app_progress_dialog(*this, plan);
|
||||
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
|
||||
return pp::panopainter::legacy_progress_dialog_node(dialogs->show_progress_dialog(plan));
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeMessageBox> App::message_box(const std::string &title, const std::string& text, bool cancel_button)
|
||||
{
|
||||
const auto plan = pp::app::plan_app_message_dialog(title, text, cancel_button);
|
||||
return pp::panopainter::create_legacy_app_message_dialog(*this, plan);
|
||||
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
|
||||
return pp::panopainter::legacy_message_dialog_node(dialogs->show_message_dialog(plan));
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
|
||||
@@ -143,7 +145,8 @@ std::shared_ptr<NodeInputBox> App::input_box(const std::string& title,
|
||||
LOG("input dialog skipped: %s", plan_result.status().message);
|
||||
return nullptr;
|
||||
}
|
||||
return pp::panopainter::create_legacy_app_input_dialog(*this, plan_result.value());
|
||||
const auto dialogs = pp::panopainter::make_legacy_app_dialog_factory(*this);
|
||||
return pp::panopainter::legacy_input_dialog_node(dialogs->show_input_dialog(plan_result.value()));
|
||||
}
|
||||
|
||||
void App::dialog_usermanual()
|
||||
|
||||
@@ -8,46 +8,150 @@
|
||||
#include "node_progress_bar.h"
|
||||
|
||||
namespace pp::panopainter {
|
||||
namespace {
|
||||
|
||||
class LegacyAppProgressDialog final : public pp::app::AppProgressDialog {
|
||||
public:
|
||||
explicit LegacyAppProgressDialog(std::shared_ptr<NodeProgressBar> node) noexcept
|
||||
: node_(std::move(node))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||
{
|
||||
return pp::app::AppDialogKind::progress;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeProgressBar> node() const noexcept
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<NodeProgressBar> node_;
|
||||
};
|
||||
|
||||
class LegacyAppMessageDialog final : public pp::app::AppMessageDialog {
|
||||
public:
|
||||
explicit LegacyAppMessageDialog(std::shared_ptr<NodeMessageBox> node) noexcept
|
||||
: node_(std::move(node))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||
{
|
||||
return pp::app::AppDialogKind::message;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeMessageBox> node() const noexcept
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<NodeMessageBox> node_;
|
||||
};
|
||||
|
||||
class LegacyAppInputDialog final : public pp::app::AppInputDialog {
|
||||
public:
|
||||
explicit LegacyAppInputDialog(std::shared_ptr<NodeInputBox> node) noexcept
|
||||
: node_(std::move(node))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::app::AppDialogKind kind() const noexcept override
|
||||
{
|
||||
return pp::app::AppDialogKind::input;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeInputBox> node() const noexcept
|
||||
{
|
||||
return node_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<NodeInputBox> node_;
|
||||
};
|
||||
|
||||
class LegacyAppDialogFactory final : public pp::app::AppDialogFactory {
|
||||
public:
|
||||
explicit LegacyAppDialogFactory(App& app) noexcept
|
||||
: app_(app)
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<pp::app::AppProgressDialog> show_progress_dialog(
|
||||
const pp::app::AppProgressDialogPlan& plan) override
|
||||
{
|
||||
return std::make_shared<LegacyAppProgressDialog>(
|
||||
create_legacy_progress_dialog_overlay(app_, plan));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<pp::app::AppMessageDialog> show_message_dialog(
|
||||
const pp::app::AppMessageDialogPlan& plan) override
|
||||
{
|
||||
return std::make_shared<LegacyAppMessageDialog>(
|
||||
create_legacy_message_dialog_overlay(app_, plan));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::shared_ptr<pp::app::AppInputDialog> show_input_dialog(
|
||||
const pp::app::AppInputDialogPlan& plan) override
|
||||
{
|
||||
return std::make_shared<LegacyAppInputDialog>(
|
||||
create_legacy_input_dialog_overlay(app_, plan));
|
||||
}
|
||||
|
||||
private:
|
||||
App& app_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<pp::app::AppDialogFactory> make_legacy_app_dialog_factory(App& app)
|
||||
{
|
||||
return std::make_unique<LegacyAppDialogFactory>(app);
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeProgressBar> create_legacy_app_progress_dialog(
|
||||
App& app,
|
||||
const pp::app::AppProgressDialogPlan& plan)
|
||||
{
|
||||
auto progress = make_legacy_overlay_node<NodeProgressBar>(app);
|
||||
progress->m_progress->SetWidthP(plan.progress_fraction);
|
||||
progress->m_title->set_text(plan.title.c_str());
|
||||
progress->m_total = plan.total;
|
||||
progress->m_count = plan.count;
|
||||
(void)attach_legacy_overlay_node(app, progress);
|
||||
return progress;
|
||||
return legacy_progress_dialog_node(make_legacy_app_dialog_factory(app)->show_progress_dialog(plan));
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeMessageBox> create_legacy_app_message_dialog(
|
||||
App& app,
|
||||
const pp::app::AppMessageDialogPlan& plan)
|
||||
{
|
||||
auto message = make_legacy_overlay_node<NodeMessageBox>(app);
|
||||
message->m_title->set_text(plan.title.c_str());
|
||||
message->m_message->set_text(plan.message.c_str());
|
||||
message->btn_ok->m_text->set_text(plan.ok_caption.c_str());
|
||||
if (plan.show_cancel)
|
||||
message->btn_cancel->m_text->set_text(plan.cancel_caption.c_str());
|
||||
else
|
||||
close_legacy_dialog_node(*message->btn_cancel);
|
||||
(void)attach_legacy_overlay_node(app, message);
|
||||
return message;
|
||||
return legacy_message_dialog_node(make_legacy_app_dialog_factory(app)->show_message_dialog(plan));
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeInputBox> create_legacy_app_input_dialog(
|
||||
App& app,
|
||||
const pp::app::AppInputDialogPlan& plan)
|
||||
{
|
||||
auto input = make_legacy_overlay_node<NodeInputBox>(app);
|
||||
input->m_title->set_text(plan.title.c_str());
|
||||
input->m_field_name->set_text(plan.field_name.c_str());
|
||||
input->btn_ok->m_text->set_text(plan.ok_caption.c_str());
|
||||
(void)attach_legacy_overlay_node(app, input);
|
||||
return input;
|
||||
return legacy_input_dialog_node(make_legacy_app_dialog_factory(app)->show_input_dialog(plan));
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeProgressBar> legacy_progress_dialog_node(
|
||||
const std::shared_ptr<pp::app::AppProgressDialog>& dialog) noexcept
|
||||
{
|
||||
auto legacy = std::dynamic_pointer_cast<LegacyAppProgressDialog>(dialog);
|
||||
return legacy ? legacy->node() : nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeMessageBox> legacy_message_dialog_node(
|
||||
const std::shared_ptr<pp::app::AppMessageDialog>& dialog) noexcept
|
||||
{
|
||||
auto legacy = std::dynamic_pointer_cast<LegacyAppMessageDialog>(dialog);
|
||||
return legacy ? legacy->node() : nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeInputBox> legacy_input_dialog_node(
|
||||
const std::shared_ptr<pp::app::AppInputDialog>& dialog) noexcept
|
||||
{
|
||||
auto legacy = std::dynamic_pointer_cast<LegacyAppInputDialog>(dialog);
|
||||
return legacy ? legacy->node() : nullptr;
|
||||
}
|
||||
|
||||
} // namespace pp::panopainter
|
||||
|
||||
@@ -11,16 +11,27 @@ class NodeProgressBar;
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
std::shared_ptr<NodeProgressBar> create_legacy_app_progress_dialog(
|
||||
[[nodiscard]] std::unique_ptr<pp::app::AppDialogFactory> make_legacy_app_dialog_factory(App& app);
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeProgressBar> create_legacy_app_progress_dialog(
|
||||
App& app,
|
||||
const pp::app::AppProgressDialogPlan& plan);
|
||||
|
||||
std::shared_ptr<NodeMessageBox> create_legacy_app_message_dialog(
|
||||
[[nodiscard]] std::shared_ptr<NodeMessageBox> create_legacy_app_message_dialog(
|
||||
App& app,
|
||||
const pp::app::AppMessageDialogPlan& plan);
|
||||
|
||||
std::shared_ptr<NodeInputBox> create_legacy_app_input_dialog(
|
||||
[[nodiscard]] std::shared_ptr<NodeInputBox> create_legacy_app_input_dialog(
|
||||
App& app,
|
||||
const pp::app::AppInputDialogPlan& plan);
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeProgressBar> legacy_progress_dialog_node(
|
||||
const std::shared_ptr<pp::app::AppProgressDialog>& dialog) noexcept;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeMessageBox> legacy_message_dialog_node(
|
||||
const std::shared_ptr<pp::app::AppMessageDialog>& dialog) noexcept;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeInputBox> legacy_input_dialog_node(
|
||||
const std::shared_ptr<pp::app::AppInputDialog>& dialog) noexcept;
|
||||
|
||||
} // namespace pp::panopainter
|
||||
|
||||
@@ -330,6 +330,42 @@ pp::foundation::Status export_equirectangular_from_document_snapshot(
|
||||
services);
|
||||
}
|
||||
|
||||
pp::foundation::Status export_depth_from_document_snapshot(
|
||||
App& app,
|
||||
const pp::app::DocumentDepthExportTarget& target,
|
||||
const LegacyDocumentExportSnapshotReports& reports)
|
||||
{
|
||||
auto exported = pp::paint_renderer::export_document_depth_pngs(
|
||||
pp::paint_renderer::DocumentDepthExportRenderPlanRequest {
|
||||
.document = &reports.snapshot.document,
|
||||
.frame_index = reports.snapshot.document.active_frame_index(),
|
||||
});
|
||||
if (!exported) {
|
||||
return exported.status();
|
||||
}
|
||||
|
||||
LOG(
|
||||
"export-depth document export PNG writer: %ux%u imageBytes=%llu depthBytes=%llu mergedFaceDraws=%zu layerDepthDraws=%zu visitedLayers=%zu visibleLayers=%zu facePayloads=%zu",
|
||||
exported.value().output_extent.width,
|
||||
exported.value().output_extent.height,
|
||||
static_cast<unsigned long long>(exported.value().image_encoded_bytes),
|
||||
static_cast<unsigned long long>(exported.value().depth_encoded_bytes),
|
||||
exported.value().merged_face_draw_count,
|
||||
exported.value().layer_depth_draw_count,
|
||||
exported.value().visited_layer_count,
|
||||
exported.value().visible_layer_count,
|
||||
exported.value().face_payload_count);
|
||||
|
||||
LegacyExportWriteServices services(app);
|
||||
return pp::app::execute_document_depth_export_write(
|
||||
target,
|
||||
pp::app::DocumentDepthExportPayload {
|
||||
.image_bytes = std::span<const std::byte>(exported.value().image_png),
|
||||
.depth_bytes = std::span<const std::byte>(exported.value().depth_png),
|
||||
},
|
||||
services);
|
||||
}
|
||||
|
||||
class LegacyDocumentExportServices final : public pp::app::DocumentExportServices {
|
||||
public:
|
||||
explicit LegacyDocumentExportServices(App& app) noexcept
|
||||
@@ -565,15 +601,29 @@ public:
|
||||
|
||||
const auto prepared = prepare_legacy_document_export_snapshot(app_, "export-depth");
|
||||
if (prepared) {
|
||||
const auto report = pp::app::make_document_canvas_save_snapshot_report(prepared.value().snapshot);
|
||||
const auto route = pp::app::plan_document_export_snapshot_route_for_current_platform(
|
||||
pp::app::DocumentExportExecutionKind::depth,
|
||||
report);
|
||||
if (!route.uses_document_snapshot_writer) {
|
||||
LOG(
|
||||
"export-depth document export writer retained legacy export: %.*s",
|
||||
static_cast<int>(route.fallback_reason.size()),
|
||||
route.fallback_reason.data());
|
||||
if (should_use_document_snapshot_writer(
|
||||
"export-depth",
|
||||
pp::app::DocumentExportExecutionKind::depth,
|
||||
prepared.value(),
|
||||
{})) {
|
||||
if (target) {
|
||||
const auto exported = export_depth_from_document_snapshot(app_, target.value(), prepared.value());
|
||||
if (exported.ok()) {
|
||||
show_export_success_dialog(
|
||||
app_,
|
||||
pp::app::plan_document_export_success_dialog(
|
||||
pp::app::DocumentExportSuccessKind::depth,
|
||||
pp::app::document_export_media_platform_destination(),
|
||||
app_.work_path));
|
||||
return;
|
||||
}
|
||||
|
||||
LOG("export-depth document export writer retained legacy export after failure: %s", exported.message);
|
||||
} else {
|
||||
LOG(
|
||||
"export-depth document export writer retained legacy export after target failure: %s",
|
||||
target.status().message);
|
||||
}
|
||||
}
|
||||
#if !__WEB__
|
||||
const auto plan = pp::paint_renderer::plan_document_depth_export_render(
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
|
||||
#include "app.h"
|
||||
#include "node.h"
|
||||
#include "node_input_box.h"
|
||||
#include "node_message_box.h"
|
||||
#include "node_popup_menu.h"
|
||||
#include "node_progress_bar.h"
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
@@ -148,4 +151,45 @@ pp::foundation::Result<std::shared_ptr<NodePopupMenu>> add_legacy_popup_menu(
|
||||
return pp::foundation::Result<std::shared_ptr<NodePopupMenu>>::success(popup);
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeProgressBar> create_legacy_progress_dialog_overlay(
|
||||
App& app,
|
||||
const pp::app::AppProgressDialogPlan& plan)
|
||||
{
|
||||
auto progress = make_legacy_overlay_node<NodeProgressBar>(app);
|
||||
progress->m_progress->SetWidthP(plan.progress_fraction);
|
||||
progress->m_title->set_text(plan.title.c_str());
|
||||
progress->m_total = plan.total;
|
||||
progress->m_count = plan.count;
|
||||
(void)attach_legacy_overlay_node(app, progress);
|
||||
return progress;
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeMessageBox> create_legacy_message_dialog_overlay(
|
||||
App& app,
|
||||
const pp::app::AppMessageDialogPlan& plan)
|
||||
{
|
||||
auto message = make_legacy_overlay_node<NodeMessageBox>(app);
|
||||
message->m_title->set_text(plan.title.c_str());
|
||||
message->m_message->set_text(plan.message.c_str());
|
||||
message->btn_ok->m_text->set_text(plan.ok_caption.c_str());
|
||||
if (plan.show_cancel)
|
||||
message->btn_cancel->m_text->set_text(plan.cancel_caption.c_str());
|
||||
else
|
||||
close_legacy_dialog_node(*message->btn_cancel);
|
||||
(void)attach_legacy_overlay_node(app, message);
|
||||
return message;
|
||||
}
|
||||
|
||||
std::shared_ptr<NodeInputBox> create_legacy_input_dialog_overlay(
|
||||
App& app,
|
||||
const pp::app::AppInputDialogPlan& plan)
|
||||
{
|
||||
auto input = make_legacy_overlay_node<NodeInputBox>(app);
|
||||
input->m_title->set_text(plan.title.c_str());
|
||||
input->m_field_name->set_text(plan.field_name.c_str());
|
||||
input->btn_ok->m_text->set_text(plan.ok_caption.c_str());
|
||||
(void)attach_legacy_overlay_node(app, input);
|
||||
return input;
|
||||
}
|
||||
|
||||
} // namespace pp::panopainter
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "app_core/app_dialog.h"
|
||||
#include "foundation/result.h"
|
||||
#include "node.h"
|
||||
|
||||
@@ -7,7 +8,10 @@
|
||||
#include <memory>
|
||||
|
||||
class App;
|
||||
class NodeInputBox;
|
||||
class NodeMessageBox;
|
||||
class NodePopupMenu;
|
||||
class NodeProgressBar;
|
||||
|
||||
namespace pp::panopainter {
|
||||
|
||||
@@ -40,6 +44,18 @@ void close_legacy_popup_panel(
|
||||
float y,
|
||||
float rtl_anchor_width) noexcept;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeProgressBar> create_legacy_progress_dialog_overlay(
|
||||
App& app,
|
||||
const pp::app::AppProgressDialogPlan& plan);
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeMessageBox> create_legacy_message_dialog_overlay(
|
||||
App& app,
|
||||
const pp::app::AppMessageDialogPlan& plan);
|
||||
|
||||
[[nodiscard]] std::shared_ptr<NodeInputBox> create_legacy_input_dialog_overlay(
|
||||
App& app,
|
||||
const pp::app::AppInputDialogPlan& plan);
|
||||
|
||||
template <class T>
|
||||
std::shared_ptr<T> make_legacy_overlay_node(App& app)
|
||||
{
|
||||
|
||||
@@ -10,6 +10,12 @@
|
||||
|
||||
namespace pp::paint_renderer {
|
||||
|
||||
pp::foundation::Result<DocumentFrameCompositeResult> composite_document_layer_frame(
|
||||
const pp::document::CanvasDocument& document,
|
||||
std::size_t layer_index,
|
||||
std::size_t frame_index,
|
||||
pp::paint::Rgba clear_color);
|
||||
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] bool is_valid_blend_mode(pp::paint::BlendMode mode) noexcept
|
||||
@@ -122,6 +128,8 @@ struct CubeFaceSample {
|
||||
float t = 0.0F;
|
||||
};
|
||||
|
||||
constexpr float document_depth_export_default_fov_degrees = 85.0F;
|
||||
|
||||
[[nodiscard]] CubeFaceSample panopainter_cube_face_sample(float x, float y, float z) noexcept
|
||||
{
|
||||
const auto ax = std::fabs(x);
|
||||
@@ -279,6 +287,56 @@ struct EquirectangularProjectionResult {
|
||||
std::size_t composited_layer_face_count = 0;
|
||||
};
|
||||
|
||||
struct PerspectiveProjectionMap {
|
||||
pp::renderer::Extent2D output_extent {};
|
||||
std::vector<CubeFaceSample> samples;
|
||||
};
|
||||
|
||||
pp::foundation::Result<PerspectiveProjectionMap> make_perspective_projection_map(
|
||||
pp::renderer::Extent2D output_extent,
|
||||
float vertical_fov_degrees)
|
||||
{
|
||||
if (!std::isfinite(vertical_fov_degrees)) {
|
||||
return pp::foundation::Result<PerspectiveProjectionMap>::failure(
|
||||
pp::foundation::Status::invalid_argument("document depth export field of view must be finite"));
|
||||
}
|
||||
if (vertical_fov_degrees <= 0.0F || vertical_fov_degrees >= 180.0F) {
|
||||
return pp::foundation::Result<PerspectiveProjectionMap>::failure(
|
||||
pp::foundation::Status::out_of_range("document depth export field of view must be between 0 and 180"));
|
||||
}
|
||||
|
||||
const auto output_pixel_count = expected_pixel_count(output_extent);
|
||||
if (!output_pixel_count) {
|
||||
return pp::foundation::Result<PerspectiveProjectionMap>::failure(output_pixel_count.status());
|
||||
}
|
||||
|
||||
PerspectiveProjectionMap map;
|
||||
map.output_extent = output_extent;
|
||||
map.samples.reserve(output_pixel_count.value());
|
||||
|
||||
constexpr auto pi = 3.14159265358979323846F;
|
||||
const auto tan_half_fov = std::tan(vertical_fov_degrees * pi / 360.0F);
|
||||
const auto aspect = static_cast<float>(output_extent.width) / static_cast<float>(output_extent.height);
|
||||
for (std::uint32_t y = 0; y < output_extent.height; ++y) {
|
||||
const auto ny = 1.0F
|
||||
- ((static_cast<float>(y) + 0.5F) / static_cast<float>(output_extent.height)) * 2.0F;
|
||||
for (std::uint32_t x = 0; x < output_extent.width; ++x) {
|
||||
const auto nx = ((static_cast<float>(x) + 0.5F) / static_cast<float>(output_extent.width)) * 2.0F
|
||||
- 1.0F;
|
||||
const auto dir_x = nx * aspect * tan_half_fov;
|
||||
const auto dir_y = ny * tan_half_fov;
|
||||
constexpr auto dir_z = -1.0F;
|
||||
const auto inv_length = 1.0F / std::sqrt(dir_x * dir_x + dir_y * dir_y + dir_z * dir_z);
|
||||
map.samples.push_back(panopainter_cube_face_sample(
|
||||
dir_x * inv_length,
|
||||
dir_y * inv_length,
|
||||
dir_z * inv_length));
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Result<PerspectiveProjectionMap>::success(std::move(map));
|
||||
}
|
||||
|
||||
pp::foundation::Result<EquirectangularProjectionResult> project_document_frame_equirectangular(
|
||||
const DocumentFrameCompositeResult& composite)
|
||||
{
|
||||
@@ -340,6 +398,87 @@ pp::foundation::Result<EquirectangularProjectionResult> project_document_frame_e
|
||||
return pp::foundation::Result<EquirectangularProjectionResult>::success(std::move(result));
|
||||
}
|
||||
|
||||
pp::foundation::Result<std::vector<pp::paint::Rgba>> project_document_frame_perspective(
|
||||
const DocumentFrameCompositeResult& composite,
|
||||
const PerspectiveProjectionMap& projection)
|
||||
{
|
||||
const auto face_pixel_count = expected_pixel_count(composite.extent);
|
||||
if (!face_pixel_count) {
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(face_pixel_count.status());
|
||||
}
|
||||
|
||||
for (const auto& face : composite.faces) {
|
||||
if (face.extent.width != composite.extent.width || face.extent.height != composite.extent.height
|
||||
|| face.pixels.size() != face_pixel_count.value()) {
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(
|
||||
pp::foundation::Status::invalid_argument("document depth export requires complete cube faces"));
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<pp::paint::Rgba> pixels;
|
||||
pixels.reserve(projection.samples.size());
|
||||
for (const auto& sample : projection.samples) {
|
||||
pixels.push_back(sample_face_nearest(composite.faces[sample.face_index], sample.s, sample.t));
|
||||
}
|
||||
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::success(std::move(pixels));
|
||||
}
|
||||
|
||||
float sample_face_alpha_nearest(
|
||||
const DocumentFaceCompositeResult& face,
|
||||
float s,
|
||||
float t) noexcept
|
||||
{
|
||||
return sample_face_nearest(face, s, t).a;
|
||||
}
|
||||
|
||||
pp::foundation::Result<std::vector<pp::paint::Rgba>> project_document_depth_perspective(
|
||||
const pp::document::CanvasDocument& document,
|
||||
std::size_t frame_index,
|
||||
const PerspectiveProjectionMap& projection)
|
||||
{
|
||||
std::vector<pp::paint::Rgba> pixels(
|
||||
projection.samples.size(),
|
||||
pp::paint::Rgba {
|
||||
.r = 0.0F,
|
||||
.g = 0.0F,
|
||||
.b = 0.0F,
|
||||
.a = 1.0F,
|
||||
});
|
||||
const auto layer_count = document.layers().size();
|
||||
for (std::size_t layer_index = 0; layer_index < layer_count; ++layer_index) {
|
||||
const auto& layer = document.layers()[layer_index];
|
||||
if (!layer.visible || layer.opacity == 0.0F || frame_index >= layer.frames.size()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto composite = composite_document_layer_frame(document, layer_index, frame_index, {});
|
||||
if (!composite) {
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::failure(composite.status());
|
||||
}
|
||||
|
||||
const auto gray = static_cast<float>(layer_index + 1U) / static_cast<float>(layer_count + 1U);
|
||||
const pp::paint::Rgba layer_color {
|
||||
.r = gray,
|
||||
.g = gray,
|
||||
.b = gray,
|
||||
.a = 1.0F,
|
||||
};
|
||||
for (std::size_t pixel_index = 0; pixel_index < projection.samples.size(); ++pixel_index) {
|
||||
const auto& sample = projection.samples[pixel_index];
|
||||
const auto alpha = sample_face_alpha_nearest(
|
||||
composite.value().faces[sample.face_index],
|
||||
sample.s,
|
||||
sample.t);
|
||||
if (alpha > 0.01F) {
|
||||
pixels[pixel_index] = layer_color;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pp::foundation::Result<std::vector<pp::paint::Rgba>>::success(std::move(pixels));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
pp::foundation::Status composite_layer(
|
||||
@@ -806,6 +945,79 @@ pp::foundation::Result<DocumentDepthExportRenderPlan> plan_document_depth_export
|
||||
return pp::foundation::Result<DocumentDepthExportRenderPlan>::success(plan);
|
||||
}
|
||||
|
||||
pp::foundation::Result<DocumentDepthPngExportResult> export_document_depth_pngs(
|
||||
DocumentDepthExportRenderPlanRequest request)
|
||||
{
|
||||
auto plan = plan_document_depth_export_render(request);
|
||||
if (!plan) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(plan.status());
|
||||
}
|
||||
|
||||
auto projection = make_perspective_projection_map(
|
||||
plan.value().output_extent,
|
||||
document_depth_export_default_fov_degrees);
|
||||
if (!projection) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(projection.status());
|
||||
}
|
||||
|
||||
auto image_composite = composite_document_frame(DocumentFrameCompositeRequest {
|
||||
.document = request.document,
|
||||
.frame_index = request.frame_index,
|
||||
.clear_color = pp::paint::Rgba {
|
||||
.r = 0.0F,
|
||||
.g = 0.0F,
|
||||
.b = 0.0F,
|
||||
.a = 1.0F,
|
||||
},
|
||||
});
|
||||
if (!image_composite) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_composite.status());
|
||||
}
|
||||
|
||||
auto image_pixels = project_document_frame_perspective(image_composite.value(), projection.value());
|
||||
if (!image_pixels) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_pixels.status());
|
||||
}
|
||||
|
||||
auto depth_pixels = project_document_depth_perspective(*request.document, request.frame_index, projection.value());
|
||||
if (!depth_pixels) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(depth_pixels.status());
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> rgba8;
|
||||
append_rgba8_bytes(rgba8, image_pixels.value());
|
||||
auto image_png = pp::assets::encode_png_rgba8(
|
||||
plan.value().output_extent.width,
|
||||
plan.value().output_extent.height,
|
||||
rgba8);
|
||||
if (!image_png) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(image_png.status());
|
||||
}
|
||||
|
||||
append_rgba8_bytes(rgba8, depth_pixels.value());
|
||||
auto depth_png = pp::assets::encode_png_rgba8(
|
||||
plan.value().output_extent.width,
|
||||
plan.value().output_extent.height,
|
||||
rgba8);
|
||||
if (!depth_png) {
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::failure(depth_png.status());
|
||||
}
|
||||
|
||||
DocumentDepthPngExportResult result;
|
||||
result.output_extent = plan.value().output_extent;
|
||||
result.image_encoded_bytes = static_cast<std::uint64_t>(image_png.value().size());
|
||||
result.depth_encoded_bytes = static_cast<std::uint64_t>(depth_png.value().size());
|
||||
result.merged_face_draw_count = plan.value().merged_face_draw_count;
|
||||
result.layer_depth_draw_count = plan.value().layer_depth_draw_count;
|
||||
result.visited_layer_count = plan.value().visited_layer_count;
|
||||
result.visible_layer_count = plan.value().visible_layer_count;
|
||||
result.face_payload_count = plan.value().face_payload_count;
|
||||
result.uses_perspective_camera = plan.value().uses_perspective_camera;
|
||||
result.image_png = std::move(image_png.value());
|
||||
result.depth_png = std::move(depth_png.value());
|
||||
return pp::foundation::Result<DocumentDepthPngExportResult>::success(std::move(result));
|
||||
}
|
||||
|
||||
pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
|
||||
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request)
|
||||
{
|
||||
|
||||
@@ -189,6 +189,23 @@ struct DocumentDepthExportRenderPlan {
|
||||
bool requires_renderer_readback = true;
|
||||
};
|
||||
|
||||
struct DocumentDepthPngExportResult {
|
||||
pp::renderer::Extent2D output_extent {
|
||||
.width = 1024,
|
||||
.height = 1024,
|
||||
};
|
||||
std::vector<std::byte> image_png;
|
||||
std::vector<std::byte> depth_png;
|
||||
std::uint64_t image_encoded_bytes = 0;
|
||||
std::uint64_t depth_encoded_bytes = 0;
|
||||
std::size_t merged_face_draw_count = 0;
|
||||
std::size_t layer_depth_draw_count = 0;
|
||||
std::size_t visited_layer_count = 0;
|
||||
std::size_t visible_layer_count = 0;
|
||||
std::size_t face_payload_count = 0;
|
||||
bool uses_perspective_camera = true;
|
||||
};
|
||||
|
||||
struct DocumentLayerEquirectangularPngExportRequest {
|
||||
const pp::document::CanvasDocument* document = nullptr;
|
||||
std::size_t frame_index = 0;
|
||||
@@ -278,6 +295,9 @@ export_document_frame_equirectangular_jpeg(
|
||||
[[nodiscard]] pp::foundation::Result<DocumentDepthExportRenderPlan> plan_document_depth_export_render(
|
||||
DocumentDepthExportRenderPlanRequest request) noexcept;
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<DocumentDepthPngExportResult> export_document_depth_pngs(
|
||||
DocumentDepthExportRenderPlanRequest request);
|
||||
|
||||
[[nodiscard]] pp::foundation::Result<DocumentLayerEquirectangularPngExportResult>
|
||||
export_document_layers_equirectangular_pngs(DocumentLayerEquirectangularPngExportRequest request);
|
||||
|
||||
|
||||
128
src/platform_apple/apple_platform_services.cpp
Normal file
128
src/platform_apple/apple_platform_services.cpp
Normal file
@@ -0,0 +1,128 @@
|
||||
#include "platform_apple/apple_platform_services.h"
|
||||
|
||||
#include "app_core/document_platform_io.h"
|
||||
#include "platform_api/platform_policy.h"
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
|
||||
namespace pp::platform::apple {
|
||||
namespace {
|
||||
|
||||
[[nodiscard]] std::vector<std::string> apple_image_file_types()
|
||||
{
|
||||
static constexpr std::array<std::string_view, 5> kFileTypes = {
|
||||
"png",
|
||||
"PNG",
|
||||
"jpg",
|
||||
"JPG",
|
||||
"jpeg",
|
||||
};
|
||||
|
||||
return { kFileTypes.begin(), kFileTypes.end() };
|
||||
}
|
||||
|
||||
void invoke_picked_path_if_selected(
|
||||
const std::string& path,
|
||||
const PickedPathCallback& callback)
|
||||
{
|
||||
if (pp::app::plan_picked_path(path) == pp::app::PickedPathAction::invoke_callback)
|
||||
callback(path);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
AppleDocumentPlatformServices::AppleDocumentPlatformServices(
|
||||
PlatformFamily family,
|
||||
AppleDocumentPickerBridge bridge)
|
||||
: family_(family)
|
||||
, bridge_(std::move(bridge))
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::string> AppleDocumentPlatformServices::document_browse_roots(
|
||||
std::string_view work_path,
|
||||
std::string_view data_path) const
|
||||
{
|
||||
return platform_document_browse_roots(family_, work_path, data_path);
|
||||
}
|
||||
|
||||
void AppleDocumentPlatformServices::pick_image(PickedPathCallback callback) const
|
||||
{
|
||||
if (family_ == PlatformFamily::ios)
|
||||
{
|
||||
if (bridge_.pick_image)
|
||||
bridge_.pick_image(std::move(callback));
|
||||
return;
|
||||
}
|
||||
|
||||
if (family_ == PlatformFamily::macos && bridge_.pick_file)
|
||||
{
|
||||
bridge_.pick_file(
|
||||
apple_image_file_types(),
|
||||
[callback = std::move(callback)](std::string path) {
|
||||
invoke_picked_path_if_selected(path, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AppleDocumentPlatformServices::pick_file(
|
||||
std::vector<std::string> file_types,
|
||||
PickedPathCallback callback) const
|
||||
{
|
||||
if (!bridge_.pick_file)
|
||||
return;
|
||||
|
||||
if (family_ == PlatformFamily::ios)
|
||||
{
|
||||
bridge_.pick_file(std::move(file_types), std::move(callback));
|
||||
return;
|
||||
}
|
||||
|
||||
if (family_ == PlatformFamily::macos)
|
||||
{
|
||||
bridge_.pick_file(
|
||||
std::move(file_types),
|
||||
[callback = std::move(callback)](std::string path) {
|
||||
invoke_picked_path_if_selected(path, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AppleDocumentPlatformServices::pick_save_file(
|
||||
std::vector<std::string> file_types,
|
||||
PickedPathCallback callback) const
|
||||
{
|
||||
if (family_ == PlatformFamily::macos && bridge_.pick_save_file)
|
||||
{
|
||||
bridge_.pick_save_file(
|
||||
std::move(file_types),
|
||||
[callback = std::move(callback)](std::string path) {
|
||||
invoke_picked_path_if_selected(path, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void AppleDocumentPlatformServices::pick_directory(PickedPathCallback callback) const
|
||||
{
|
||||
if (family_ == PlatformFamily::macos && bridge_.pick_directory)
|
||||
{
|
||||
bridge_.pick_directory([callback = std::move(callback)](std::string path) {
|
||||
invoke_picked_path_if_selected(path, callback);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bool AppleDocumentPlatformServices::supports_working_directory_picker() const
|
||||
{
|
||||
return platform_supports_working_directory_picker(family_);
|
||||
}
|
||||
|
||||
std::string AppleDocumentPlatformServices::format_working_directory_path(std::string_view path) const
|
||||
{
|
||||
if (family_ == PlatformFamily::macos && bridge_.format_working_directory_path)
|
||||
return bridge_.format_working_directory_path(path);
|
||||
return std::string(path);
|
||||
}
|
||||
|
||||
}
|
||||
44
src/platform_apple/apple_platform_services.h
Normal file
44
src/platform_apple/apple_platform_services.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
|
||||
#include "platform_api/platform_policy.h"
|
||||
#include "platform_api/platform_services.h"
|
||||
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
namespace pp::platform::apple {
|
||||
|
||||
struct AppleDocumentPickerBridge {
|
||||
std::function<void(PickedPathCallback callback)> pick_image;
|
||||
std::function<void(std::vector<std::string> file_types, PickedPathCallback callback)> pick_file;
|
||||
std::function<void(std::vector<std::string> file_types, PickedPathCallback callback)> pick_save_file;
|
||||
std::function<void(PickedPathCallback callback)> pick_directory;
|
||||
std::function<std::string(std::string_view path)> format_working_directory_path;
|
||||
};
|
||||
|
||||
class AppleDocumentPlatformServices {
|
||||
public:
|
||||
explicit AppleDocumentPlatformServices(
|
||||
PlatformFamily family,
|
||||
AppleDocumentPickerBridge bridge = {});
|
||||
|
||||
[[nodiscard]] std::vector<std::string> document_browse_roots(
|
||||
std::string_view work_path,
|
||||
std::string_view data_path) const;
|
||||
|
||||
void pick_image(PickedPathCallback callback) const;
|
||||
void pick_file(std::vector<std::string> file_types, PickedPathCallback callback) const;
|
||||
void pick_save_file(std::vector<std::string> file_types, PickedPathCallback callback) const;
|
||||
void pick_directory(PickedPathCallback callback) const;
|
||||
|
||||
[[nodiscard]] bool supports_working_directory_picker() const;
|
||||
[[nodiscard]] std::string format_working_directory_path(std::string_view path) const;
|
||||
|
||||
private:
|
||||
PlatformFamily family_;
|
||||
AppleDocumentPickerBridge bridge_;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
#include "platform_legacy/legacy_platform_services.h"
|
||||
|
||||
#include "app.h"
|
||||
#include "app_core/document_platform_io.h"
|
||||
#include "legacy_ui_gl_dispatch.h"
|
||||
#include "log.h"
|
||||
#include "platform_apple/apple_platform_services.h"
|
||||
#include "platform_api/network_tls_policy.h"
|
||||
#include "platform_api/platform_policy.h"
|
||||
#include "renderer_gl/opengl_capabilities.h"
|
||||
@@ -52,14 +52,6 @@ void webgl_sync();
|
||||
|
||||
namespace {
|
||||
|
||||
void invoke_picked_path_if_selected(
|
||||
const std::string& path,
|
||||
const std::function<void(std::string path)>& callback)
|
||||
{
|
||||
if (pp::app::plan_picked_path(path) == pp::app::PickedPathAction::invoke_callback)
|
||||
callback(path);
|
||||
}
|
||||
|
||||
#ifdef __WEB__
|
||||
class RetainedWebPlatformServices final : public pp::platform::WebPlatformServices {
|
||||
public:
|
||||
@@ -102,6 +94,79 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
#if defined(__IOS__) || defined(__OSX__)
|
||||
[[nodiscard]] NSMutableArray<NSString*>* apple_file_types_array(const std::vector<std::string>& file_types)
|
||||
{
|
||||
NSMutableArray<NSString*>* types = [NSMutableArray arrayWithCapacity:file_types.size()];
|
||||
for (const auto& type : file_types)
|
||||
{
|
||||
[types addObject:[NSString stringWithCString:type.c_str() encoding:NSUTF8StringEncoding]];
|
||||
}
|
||||
return types;
|
||||
}
|
||||
|
||||
[[nodiscard]] pp::platform::apple::AppleDocumentPlatformServices& active_apple_document_platform_services()
|
||||
{
|
||||
#ifdef __IOS__
|
||||
static pp::platform::apple::AppleDocumentPlatformServices services(
|
||||
pp::platform::PlatformFamily::ios,
|
||||
[] {
|
||||
pp::platform::apple::AppleDocumentPickerBridge bridge;
|
||||
bridge.pick_image = [](pp::platform::PickedPathCallback callback) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[App::I->ios_view pick_photo:callback];
|
||||
});
|
||||
};
|
||||
bridge.pick_file = [](
|
||||
std::vector<std::string> file_types,
|
||||
pp::platform::PickedPathCallback callback) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[App::I->ios_view pick_file:apple_file_types_array(file_types) then:callback];
|
||||
});
|
||||
};
|
||||
return bridge;
|
||||
}());
|
||||
return services;
|
||||
#else
|
||||
static pp::platform::apple::AppleDocumentPlatformServices services(
|
||||
pp::platform::PlatformFamily::macos,
|
||||
[] {
|
||||
pp::platform::apple::AppleDocumentPickerBridge bridge;
|
||||
bridge.pick_file = [](
|
||||
std::vector<std::string> file_types,
|
||||
pp::platform::PickedPathCallback callback) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
const std::string path = [App::I->osx_view pick_file:apple_file_types_array(file_types)];
|
||||
callback(path);
|
||||
});
|
||||
};
|
||||
bridge.pick_save_file = [](
|
||||
std::vector<std::string> file_types,
|
||||
pp::platform::PickedPathCallback callback) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
const std::string path = [App::I->osx_view pick_file_save:apple_file_types_array(file_types)];
|
||||
callback(path);
|
||||
});
|
||||
};
|
||||
bridge.pick_directory = [](pp::platform::PickedPathCallback callback) {
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
const std::string path = [App::I->osx_view pick_dir];
|
||||
callback(path);
|
||||
});
|
||||
};
|
||||
bridge.format_working_directory_path = [](std::string_view path) {
|
||||
char path_buffer[4096] = {};
|
||||
if (realpath(std::string(path).c_str(), path_buffer))
|
||||
return std::string(path_buffer);
|
||||
return std::string(path);
|
||||
};
|
||||
return bridge;
|
||||
}());
|
||||
return services;
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
// DEBT-0017: fallback for platforms that do not inject PlatformServices yet.
|
||||
class LegacyPlatformServices final : public pp::platform::PlatformServices {
|
||||
public:
|
||||
@@ -376,10 +441,14 @@ public:
|
||||
std::string_view work_path,
|
||||
std::string_view data_path) override
|
||||
{
|
||||
#if defined(__IOS__) || defined(__OSX__)
|
||||
return active_apple_document_platform_services().document_browse_roots(work_path, data_path);
|
||||
#else
|
||||
return pp::platform::platform_document_browse_roots(
|
||||
pp::platform::current_platform_family(),
|
||||
work_path,
|
||||
data_path);
|
||||
#endif
|
||||
}
|
||||
|
||||
void save_ui_state() override
|
||||
@@ -414,15 +483,9 @@ public:
|
||||
void pick_image(pp::platform::PickedPathCallback callback) override
|
||||
{
|
||||
#ifdef __IOS__
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[App::I->ios_view pick_photo:callback];
|
||||
});
|
||||
active_apple_document_platform_services().pick_image(std::move(callback));
|
||||
#elif __OSX__
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSArray* fileTypes = [NSArray arrayWithObjects:@"png", @"PNG", @"jpg", @"JPG", @"jpeg", nil];
|
||||
std::string path = [App::I->osx_view pick_file:fileTypes];
|
||||
invoke_picked_path_if_selected(path, callback);
|
||||
});
|
||||
active_apple_document_platform_services().pick_image(std::move(callback));
|
||||
#elif __ANDROID__
|
||||
android_pick_file(callback);
|
||||
#elif __LINUX__
|
||||
@@ -438,20 +501,9 @@ public:
|
||||
void pick_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
|
||||
{
|
||||
#ifdef __IOS__
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSMutableArray<NSString*>* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()];
|
||||
for (const auto& t : file_types)
|
||||
[fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]];
|
||||
[App::I->ios_view pick_file:fileTypes then:callback];
|
||||
});
|
||||
active_apple_document_platform_services().pick_file(std::move(file_types), std::move(callback));
|
||||
#elif __OSX__
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSMutableArray<NSString*>* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()];
|
||||
for (const auto& t : file_types)
|
||||
[fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]];
|
||||
std::string path = [App::I->osx_view pick_file:fileTypes];
|
||||
invoke_picked_path_if_selected(path, callback);
|
||||
});
|
||||
active_apple_document_platform_services().pick_file(std::move(file_types), std::move(callback));
|
||||
#elif __ANDROID__
|
||||
android_pick_file(callback);
|
||||
#elif __LINUX__
|
||||
@@ -468,13 +520,7 @@ public:
|
||||
void pick_save_file(std::vector<std::string> file_types, pp::platform::PickedPathCallback callback) override
|
||||
{
|
||||
#if __OSX__
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
NSMutableArray<NSString*>* fileTypes = [NSMutableArray arrayWithCapacity:file_types.size()];
|
||||
for (const auto& t : file_types)
|
||||
[fileTypes addObject:[NSString stringWithCString:t.c_str() encoding:NSUTF8StringEncoding]];
|
||||
std::string path = [App::I->osx_view pick_file_save:fileTypes];
|
||||
invoke_picked_path_if_selected(path, callback);
|
||||
});
|
||||
active_apple_document_platform_services().pick_save_file(std::move(file_types), std::move(callback));
|
||||
#elif __ANDROID__
|
||||
android_pick_file_save(callback);
|
||||
#else
|
||||
@@ -488,10 +534,7 @@ public:
|
||||
#ifdef __IOS__
|
||||
(void)callback;
|
||||
#elif __OSX__
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
std::string path = [App::I->osx_view pick_dir];
|
||||
invoke_picked_path_if_selected(path, callback);
|
||||
});
|
||||
active_apple_document_platform_services().pick_directory(std::move(callback));
|
||||
#elif __ANDROID__
|
||||
(void)callback;
|
||||
#else
|
||||
@@ -501,16 +544,18 @@ public:
|
||||
|
||||
[[nodiscard]] bool supports_working_directory_picker() override
|
||||
{
|
||||
#if defined(__IOS__) || defined(__OSX__)
|
||||
return active_apple_document_platform_services().supports_working_directory_picker();
|
||||
#else
|
||||
return pp::platform::platform_supports_working_directory_picker(
|
||||
pp::platform::current_platform_family());
|
||||
#endif
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string format_working_directory_path(std::string_view path) override
|
||||
{
|
||||
#if defined(__OSX__)
|
||||
char path_buffer[4096] = {};
|
||||
if (realpath(std::string(path).c_str(), path_buffer))
|
||||
return path_buffer;
|
||||
#if defined(__IOS__) || defined(__OSX__)
|
||||
return active_apple_document_platform_services().format_working_directory_path(path);
|
||||
#endif
|
||||
return std::string(path);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user