From 35ea1690fee1f01cee4beab9c7696966b16b9cd0 Mon Sep 17 00:00:00 2001 From: Fabien Freling Date: Fri, 5 Jul 2019 14:20:17 +0200 Subject: [PATCH] Display maps from OCaml --- src/core/cbindings.ml | 1 + src/core/dune | 1 + src/core/memory.ml | 11 ++++++++ src/qt/BackgroundMap.qml | 12 ++++++--- src/qt/CMakeLists.txt | 2 +- src/qt/main.cpp | 7 ++--- src/qt/main.qml | 2 ++ src/qt/oboy.cpp | 55 ++++++++++++++++++++++++++++++++++++--- src/qt/oboy.h | 4 +++ src/qt/oimageprovider.cpp | 22 ++++++++++++++++ src/qt/oimageprovider.h | 14 ++++++++++ 11 files changed, 121 insertions(+), 10 deletions(-) create mode 100644 src/qt/oimageprovider.cpp create mode 100644 src/qt/oimageprovider.h diff --git a/src/core/cbindings.ml b/src/core/cbindings.ml index d08f928..302c812 100644 --- a/src/core/cbindings.ml +++ b/src/core/cbindings.ml @@ -2,3 +2,4 @@ let () = Callback.register "oboy_name" Version.name; Callback.register "oboy_version" Version.version; Callback.register "oboy_load" State.load_cartridge; + Callback.register "oboy_bg_map" Memory.background_map; diff --git a/src/core/dune b/src/core/dune index f5c530a..c430135 100644 --- a/src/core/dune +++ b/src/core/dune @@ -3,6 +3,7 @@ ; combine static pthread with dynamic libc (executable (name cbindings) + (libraries bigarray) (modes object)) ; Standalone binary diff --git a/src/core/memory.ml b/src/core/memory.ml index bbfd9c1..cda3a48 100644 --- a/src/core/memory.ml +++ b/src/core/memory.ml @@ -6,6 +6,7 @@ * LICENSE file at the top level directory of this source tree. *) +open Bigarray open Bytes open Printf @@ -107,3 +108,13 @@ let update_timers mem cycles = (* TODO: INT 50 - Timer interupt *) end end + +let background_map () = + let bg_map = Array2.create Bigarray.int8_unsigned Bigarray.c_layout 8 8 in + Array2.fill bg_map 0; + for j = 0 to (Array2.dim2 bg_map) - 1 do + for i = 0 to (Array2.dim1 bg_map) - 1 do + bg_map.{i, j} <- (i + j * Array2.dim1 bg_map) mod 4 + done + done; + bg_map diff --git a/src/qt/BackgroundMap.qml b/src/qt/BackgroundMap.qml index a9e1c7a..2cb771d 100644 --- a/src/qt/BackgroundMap.qml +++ b/src/qt/BackgroundMap.qml @@ -12,8 +12,14 @@ Window { maximumHeight: height minimumHeight: height - BackgroundMapForm { - anchors.fill: parent - } +// BackgroundMapForm { +// anchors.fill: parent +// } + Image { + source: "image://oboy/bg_map" + anchors.fill: parent + smooth: false + fillMode: Image.PreserveAspectFit + } } diff --git a/src/qt/CMakeLists.txt b/src/qt/CMakeLists.txt index 967512b..af16a95 100644 --- a/src/qt/CMakeLists.txt +++ b/src/qt/CMakeLists.txt @@ -10,7 +10,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Qt5 COMPONENTS Core Quick REQUIRED) -add_executable(${PROJECT_NAME} "main.cpp" "oboy.cpp" "qml.qrc") +add_executable(${PROJECT_NAME} "main.cpp" "oboy.cpp" "oimageprovider.cpp" "qml.qrc") target_compile_definitions(${PROJECT_NAME} PRIVATE $<$,$>:QT_QML_DEBUG>) target_link_libraries(${PROJECT_NAME} PRIVATE Qt5::Core Qt5::Quick) diff --git a/src/qt/main.cpp b/src/qt/main.cpp index ec71e7a..f45a3e3 100644 --- a/src/qt/main.cpp +++ b/src/qt/main.cpp @@ -3,10 +3,10 @@ #include #include -#include -#include - #include "oboy.h" +#include "oimageprovider.h" + +#include int main(int argc, char *argv[]) { @@ -16,6 +16,7 @@ int main(int argc, char *argv[]) qmlRegisterType("com.oboy.oboy", 1, 0, "OBoy"); QQmlApplicationEngine engine; + engine.addImageProvider(QLatin1String("oboy"), new OImageProvider(engine)); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); caml_main(argv); diff --git a/src/qt/main.qml b/src/qt/main.qml index 168b195..a023a96 100644 --- a/src/qt/main.qml +++ b/src/qt/main.qml @@ -27,6 +27,7 @@ ApplicationWindow { title: qsTr("View") MenuItem { text: qsTr("Background maps") + enabled: oboy.loaded onTriggered: { console.log("Background maps"); var component = Qt.createComponent("BackgroundMap.qml") @@ -52,6 +53,7 @@ ApplicationWindow { OBoy { id: oboy + objectName: "qml_oboy" } MainForm { diff --git a/src/qt/oboy.cpp b/src/qt/oboy.cpp index 0575eee..a621458 100644 --- a/src/qt/oboy.cpp +++ b/src/qt/oboy.cpp @@ -1,15 +1,26 @@ #include "oboy.h" +#include + #include #include #include +#include #include #include #include OBoy::OBoy(QObject *parent) : QObject(parent) { +// load("/Users/ffreling/Downloads/Super_Mario_Land.gb"); +} +value *fetch_caml_callback(const char *name) { + value * closure_f = caml_named_value(name); + if (closure_f == nullptr) { + qFatal("Cannot find OCaml function: %s", name); + } + return closure_f; } // https://caml.inria.fr/pub/docs/manual-ocaml/intfc.html @@ -19,7 +30,7 @@ QString OBoy::name() const { CAMLparam0(); - value * closure_f = caml_named_value("oboy_name"); + static value * closure_f = fetch_caml_callback("oboy_name"); if (closure_f == nullptr) { CAMLdrop; return QString(""); @@ -34,7 +45,7 @@ QString OBoy::version() const { CAMLparam0(); - value * closure_f = caml_named_value("oboy_version"); + static value * closure_f = fetch_caml_callback("oboy_version"); if (closure_f == nullptr) { CAMLdrop; return QString(""); @@ -50,7 +61,7 @@ bool OBoy::load(const QString &path) CAMLparam0(); CAMLlocal1(ocaml_path) ; - value * closure_f = caml_named_value("oboy_load"); + static value * closure_f = fetch_caml_callback("oboy_load"); if (closure_f == nullptr) { CAMLdrop; return false; @@ -72,3 +83,41 @@ bool OBoy::loaded() const { return _loaded; } + +QImage OBoy::backgroundMap() const +{ + CAMLparam0(); + + static value * closure_f = fetch_caml_callback("oboy_bg_map"); + if (closure_f == nullptr) { + CAMLdrop; + return QImage(0, 0, QImage::Format_Indexed8); + } + + const auto bg_array = Caml_ba_array_val(caml_callback(*closure_f, Val_unit)); + const auto bg_raw_data = static_cast(bg_array->data); + + Q_ASSERT(bg_array->num_dims == 2); + Q_ASSERT(bg_array->flags & CAML_BA_UINT8); + + const int width = bg_array->dim[0]; + const int height = bg_array->dim[1]; + QImage img(width, height, QImage::Format_Indexed8); + + QVector green_palette = { + qRgb(15, 56, 15), + qRgb(48, 98, 48), + qRgb(139, 172, 15), + qRgb(155, 188, 15), + }; + img.setColorTable(green_palette); + + for (int y = 0; y < height; ++y) { + for (int x = 0; x < width; ++x) { + img.setPixel(x, y, bg_raw_data[y * width + x] % 4); + } + } + + CAMLdrop; + return img; +} diff --git a/src/qt/oboy.h b/src/qt/oboy.h index 0f9c852..8e678e1 100644 --- a/src/qt/oboy.h +++ b/src/qt/oboy.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include class OBoy : public QObject { @@ -8,6 +10,7 @@ class OBoy : public QObject Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString version READ version CONSTANT) Q_PROPERTY(bool loaded READ loaded NOTIFY loadedChanged) + Q_PROPERTY(QImage backgroundMap READ backgroundMap CONSTANT) public: explicit OBoy(QObject *parent = nullptr); @@ -15,6 +18,7 @@ public: QString version() const; bool loaded() const; Q_INVOKABLE bool load(const QString &path); + QImage backgroundMap() const; signals: void loadedChanged(bool loaded); diff --git a/src/qt/oimageprovider.cpp b/src/qt/oimageprovider.cpp new file mode 100644 index 0000000..85e9833 --- /dev/null +++ b/src/qt/oimageprovider.cpp @@ -0,0 +1,22 @@ +#include "oimageprovider.h" +#include "oboy.h" + +OImageProvider::OImageProvider(QQmlApplicationEngine &engine) + : QQuickImageProvider(QQuickImageProvider::Image) + , _engine(engine) +{ + +} + +QImage OImageProvider::requestImage(const QString &id, QSize *size, const QSize &requestedSize) +{ + QList roots = _engine.rootObjects(); + OBoy *oboy = roots[0]->findChild("qml_oboy"); + if (!oboy) { + qFatal("Cannot find oboy instance."); + } + Q_ASSERT(oboy->loaded()); + + QImage img = oboy->backgroundMap(); + return img; +} diff --git a/src/qt/oimageprovider.h b/src/qt/oimageprovider.h new file mode 100644 index 0000000..0e2d689 --- /dev/null +++ b/src/qt/oimageprovider.h @@ -0,0 +1,14 @@ +#pragma once + +#include +#include + +class OImageProvider : public QQuickImageProvider +{ +public: + OImageProvider(QQmlApplicationEngine &engine); + QImage requestImage(const QString &id, QSize *size, const QSize &requestedSize) override; + +private: + QQmlApplicationEngine &_engine; +};