Compare commits
17 Commits
master
...
embedder_t
Author | SHA1 | Date |
---|---|---|
Vitaliy Zarubin | ec4b86573c | 1 year ago |
Vitaliy Zarubin | 32a481b213 | 1 year ago |
Vitaliy Zarubin | 4db615def4 | 1 year ago |
Vitaliy Zarubin | 0ccba5006e | 1 year ago |
Vitaliy Zarubin | c4556091dc | 1 year ago |
Vitaliy Zarubin | 4d898d36a5 | 1 year ago |
Vitaliy Zarubin | e6e3352e7f | 1 year ago |
Vitaliy Zarubin | f79d080cf3 | 1 year ago |
Vitaliy Zarubin | 96795f33f0 | 1 year ago |
Vitaliy Zarubin | 1761211ba1 | 1 year ago |
Vitaliy Zarubin | 4e6ff5e419 | 1 year ago |
Vitaliy Zarubin | edfa81bca0 | 1 year ago |
Vitaliy Zarubin | 013245d9bb | 1 year ago |
Vitaliy Zarubin | fed1031ab4 | 1 year ago |
Vitaliy Zarubin | 80e1758cc8 | 1 year ago |
Vitaliy Zarubin | 3413ecd9a3 | 1 year ago |
Vitaliy Zarubin | 4b7268cee0 | 1 year ago |
39 changed files with 1754 additions and 17 deletions
@ -0,0 +1,22 @@ |
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
// SPDX-License-Identifier: BSD-3-Clause |
||||||
|
import 'package:flutter/widgets.dart'; |
||||||
|
import 'package:scoped_model/scoped_model.dart'; |
||||||
|
import 'package:xdga_directories/xdga_directories.dart' as xdga; |
||||||
|
|
||||||
|
/// Model for [EmbedderTexturePage] |
||||||
|
class EmbedderTextureModel extends Model { |
||||||
|
/// Get [ScopedModel] |
||||||
|
static EmbedderTextureModel of(BuildContext context) => |
||||||
|
ScopedModel.of<EmbedderTextureModel>(context); |
||||||
|
|
||||||
|
/// Error |
||||||
|
String? _error; |
||||||
|
|
||||||
|
/// Public error |
||||||
|
String? get error => _error; |
||||||
|
|
||||||
|
/// Public is error |
||||||
|
bool get isError => _error != null; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,25 @@ |
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
// SPDX-License-Identifier: BSD-3-Clause |
||||||
|
import 'package:flutter_example_packages/base/package/package_page.dart'; |
||||||
|
import 'package:flutter_example_packages/packages/embedder_texture/page.dart'; |
||||||
|
import 'package:get_it/get_it.dart'; |
||||||
|
|
||||||
|
import 'model.dart'; |
||||||
|
|
||||||
|
/// Package values |
||||||
|
final packageEmbedderTexture = PackagePage( |
||||||
|
key: 'Embedder Texture', |
||||||
|
descEN: ''' |
||||||
|
Test Embedder texture plugin |
||||||
|
''', |
||||||
|
descRU: ''' |
||||||
|
Тестирование текстур Embedder |
||||||
|
''', |
||||||
|
version: '0.0.1', |
||||||
|
isPlatformDependent: true, |
||||||
|
page: () => EmbedderTexturePage(), |
||||||
|
init: () { |
||||||
|
GetIt.instance |
||||||
|
.registerFactory<EmbedderTextureModel>(() => EmbedderTextureModel()); |
||||||
|
}, |
||||||
|
); |
@ -0,0 +1,53 @@ |
|||||||
|
// SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
// SPDX-License-Identifier: BSD-3-Clause |
||||||
|
import 'package:flutter/material.dart'; |
||||||
|
import 'package:flutter_example_packages/base/di/app_di.dart'; |
||||||
|
import 'package:flutter_example_packages/base/package/package.dart'; |
||||||
|
import 'package:flutter_example_packages/packages/embedder_texture/model.dart'; |
||||||
|
import 'package:flutter_example_packages/packages/embedder_texture/package.dart'; |
||||||
|
import 'package:flutter_example_packages/widgets/base/export.dart'; |
||||||
|
import 'package:flutter_example_packages/widgets/blocks/block_alert.dart'; |
||||||
|
import 'package:flutter_example_packages/widgets/blocks/block_info_package.dart'; |
||||||
|
import 'package:flutter_example_packages/widgets/blocks/block_item.dart'; |
||||||
|
import 'package:flutter_example_packages/widgets/layouts/block_layout.dart'; |
||||||
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; |
||||||
|
import 'package:embedder_texture/embedder_texture.dart'; |
||||||
|
|
||||||
|
class EmbedderTexturePage extends AppStatefulWidget { |
||||||
|
EmbedderTexturePage({ |
||||||
|
super.key, |
||||||
|
}); |
||||||
|
|
||||||
|
final Package package = packageEmbedderTexture; |
||||||
|
|
||||||
|
@override |
||||||
|
State<EmbedderTexturePage> createState() => _EmbedderTexturePageState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _EmbedderTexturePageState extends AppState<EmbedderTexturePage> { |
||||||
|
@override |
||||||
|
Widget buildWide( |
||||||
|
BuildContext context, |
||||||
|
MediaQueryData media, |
||||||
|
AppLocalizations l10n, |
||||||
|
) { |
||||||
|
final width = MediaQuery.of(context).size.width; |
||||||
|
final height = MediaQuery.of(context).size.height; |
||||||
|
|
||||||
|
return BlockLayout<EmbedderTextureModel>( |
||||||
|
model: getIt<EmbedderTextureModel>(), |
||||||
|
title: widget.package.key, |
||||||
|
builder: (context, child, model) { |
||||||
|
return Padding( |
||||||
|
padding: const EdgeInsets.all(20), |
||||||
|
child: Center( |
||||||
|
child: EmbedderTexture( |
||||||
|
width: width, |
||||||
|
height: height, |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
}, |
||||||
|
); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,139 @@ |
|||||||
|
# Project-level configuration. |
||||||
|
cmake_minimum_required(VERSION 3.10) |
||||||
|
project(runner LANGUAGES CXX) |
||||||
|
|
||||||
|
# The name of the executable created for the application. Change this to change |
||||||
|
# the on-disk name of your application. |
||||||
|
set(BINARY_NAME "flutter_example_packages") |
||||||
|
# The unique GTK application identifier for this application. See: |
||||||
|
# https://wiki.gnome.org/HowDoI/ChooseApplicationID |
||||||
|
set(APPLICATION_ID "ru.auroraos.flutter_example_packages") |
||||||
|
|
||||||
|
# Explicitly opt in to modern CMake behaviors to avoid warnings with recent |
||||||
|
# versions of CMake. |
||||||
|
cmake_policy(SET CMP0063 NEW) |
||||||
|
|
||||||
|
# Load bundled libraries from the lib/ directory relative to the binary. |
||||||
|
set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") |
||||||
|
|
||||||
|
# Root filesystem for cross-building. |
||||||
|
if(FLUTTER_TARGET_PLATFORM_SYSROOT) |
||||||
|
set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) |
||||||
|
set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) |
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) |
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) |
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) |
||||||
|
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) |
||||||
|
endif() |
||||||
|
|
||||||
|
# Define build configuration options. |
||||||
|
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) |
||||||
|
set(CMAKE_BUILD_TYPE "Debug" CACHE |
||||||
|
STRING "Flutter build mode" FORCE) |
||||||
|
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS |
||||||
|
"Debug" "Profile" "Release") |
||||||
|
endif() |
||||||
|
|
||||||
|
# Compilation settings that should be applied to most targets. |
||||||
|
# |
||||||
|
# Be cautious about adding new options here, as plugins use this function by |
||||||
|
# default. In most cases, you should add new options to specific targets instead |
||||||
|
# of modifying this function. |
||||||
|
function(APPLY_STANDARD_SETTINGS TARGET) |
||||||
|
target_compile_features(${TARGET} PUBLIC cxx_std_14) |
||||||
|
# target_compile_options(${TARGET} PRIVATE -Wall -Werror) |
||||||
|
target_compile_options(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:-O3>") |
||||||
|
target_compile_definitions(${TARGET} PRIVATE "$<$<NOT:$<CONFIG:Debug>>:NDEBUG>") |
||||||
|
endfunction() |
||||||
|
|
||||||
|
# Flutter library and tool build rules. |
||||||
|
set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") |
||||||
|
add_subdirectory(${FLUTTER_MANAGED_DIR}) |
||||||
|
|
||||||
|
# System-level dependencies. |
||||||
|
find_package(PkgConfig REQUIRED) |
||||||
|
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) |
||||||
|
|
||||||
|
add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") |
||||||
|
|
||||||
|
# Define the application target. To change its name, change BINARY_NAME above, |
||||||
|
# not the value here, or `flutter run` will no longer work. |
||||||
|
# |
||||||
|
# Any new source files that you add to the application should be added here. |
||||||
|
add_executable(${BINARY_NAME} |
||||||
|
"main.cc" |
||||||
|
"my_application.cc" |
||||||
|
"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" |
||||||
|
) |
||||||
|
|
||||||
|
# Apply the standard set of build settings. This can be removed for applications |
||||||
|
# that need different build settings. |
||||||
|
apply_standard_settings(${BINARY_NAME}) |
||||||
|
|
||||||
|
# Add dependency libraries. Add any application-specific dependencies here. |
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE flutter) |
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) |
||||||
|
|
||||||
|
# Run the Flutter tool portions of the build. This must not be removed. |
||||||
|
add_dependencies(${BINARY_NAME} flutter_assemble) |
||||||
|
|
||||||
|
# Only the install-generated bundle's copy of the executable will launch |
||||||
|
# correctly, since the resources must in the right relative locations. To avoid |
||||||
|
# people trying to run the unbundled copy, put it in a subdirectory instead of |
||||||
|
# the default top-level location. |
||||||
|
set_target_properties(${BINARY_NAME} |
||||||
|
PROPERTIES |
||||||
|
RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" |
||||||
|
) |
||||||
|
|
||||||
|
|
||||||
|
# Generated plugin build rules, which manage building the plugins and adding |
||||||
|
# them to the application. |
||||||
|
include(flutter/generated_plugins.cmake) |
||||||
|
|
||||||
|
|
||||||
|
# === Installation === |
||||||
|
# By default, "installing" just makes a relocatable bundle in the build |
||||||
|
# directory. |
||||||
|
set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") |
||||||
|
if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) |
||||||
|
set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) |
||||||
|
endif() |
||||||
|
|
||||||
|
# Start with a clean build bundle directory every time. |
||||||
|
install(CODE " |
||||||
|
file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") |
||||||
|
" COMPONENT Runtime) |
||||||
|
|
||||||
|
set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") |
||||||
|
set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") |
||||||
|
|
||||||
|
install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" |
||||||
|
COMPONENT Runtime) |
||||||
|
|
||||||
|
install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" |
||||||
|
COMPONENT Runtime) |
||||||
|
|
||||||
|
install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" |
||||||
|
COMPONENT Runtime) |
||||||
|
|
||||||
|
foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) |
||||||
|
install(FILES "${bundled_library}" |
||||||
|
DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" |
||||||
|
COMPONENT Runtime) |
||||||
|
endforeach(bundled_library) |
||||||
|
|
||||||
|
# Fully re-copy the assets directory on each build to avoid having stale files |
||||||
|
# from a previous install. |
||||||
|
set(FLUTTER_ASSET_DIR_NAME "flutter_assets") |
||||||
|
install(CODE " |
||||||
|
file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") |
||||||
|
" COMPONENT Runtime) |
||||||
|
install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" |
||||||
|
DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) |
||||||
|
|
||||||
|
# Install the AOT library on non-Debug builds only. |
||||||
|
if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") |
||||||
|
install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" |
||||||
|
COMPONENT Runtime) |
||||||
|
endif() |
@ -0,0 +1,88 @@ |
|||||||
|
# This file controls Flutter-level build steps. It should not be edited. |
||||||
|
cmake_minimum_required(VERSION 3.10) |
||||||
|
|
||||||
|
set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") |
||||||
|
|
||||||
|
# Configuration provided via flutter tool. |
||||||
|
include(${EPHEMERAL_DIR}/generated_config.cmake) |
||||||
|
|
||||||
|
# TODO: Move the rest of this into files in ephemeral. See |
||||||
|
# https://github.com/flutter/flutter/issues/57146. |
||||||
|
|
||||||
|
# Serves the same purpose as list(TRANSFORM ... PREPEND ...), |
||||||
|
# which isn't available in 3.10. |
||||||
|
function(list_prepend LIST_NAME PREFIX) |
||||||
|
set(NEW_LIST "") |
||||||
|
foreach(element ${${LIST_NAME}}) |
||||||
|
list(APPEND NEW_LIST "${PREFIX}${element}") |
||||||
|
endforeach(element) |
||||||
|
set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) |
||||||
|
endfunction() |
||||||
|
|
||||||
|
# === Flutter Library === |
||||||
|
# System-level dependencies. |
||||||
|
find_package(PkgConfig REQUIRED) |
||||||
|
pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) |
||||||
|
pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) |
||||||
|
pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) |
||||||
|
|
||||||
|
set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") |
||||||
|
|
||||||
|
# Published to parent scope for install step. |
||||||
|
set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) |
||||||
|
set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) |
||||||
|
set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) |
||||||
|
set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) |
||||||
|
|
||||||
|
list(APPEND FLUTTER_LIBRARY_HEADERS |
||||||
|
"fl_basic_message_channel.h" |
||||||
|
"fl_binary_codec.h" |
||||||
|
"fl_binary_messenger.h" |
||||||
|
"fl_dart_project.h" |
||||||
|
"fl_engine.h" |
||||||
|
"fl_json_message_codec.h" |
||||||
|
"fl_json_method_codec.h" |
||||||
|
"fl_message_codec.h" |
||||||
|
"fl_method_call.h" |
||||||
|
"fl_method_channel.h" |
||||||
|
"fl_method_codec.h" |
||||||
|
"fl_method_response.h" |
||||||
|
"fl_plugin_registrar.h" |
||||||
|
"fl_plugin_registry.h" |
||||||
|
"fl_standard_message_codec.h" |
||||||
|
"fl_standard_method_codec.h" |
||||||
|
"fl_string_codec.h" |
||||||
|
"fl_value.h" |
||||||
|
"fl_view.h" |
||||||
|
"flutter_linux.h" |
||||||
|
) |
||||||
|
list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") |
||||||
|
add_library(flutter INTERFACE) |
||||||
|
target_include_directories(flutter INTERFACE |
||||||
|
"${EPHEMERAL_DIR}" |
||||||
|
) |
||||||
|
target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") |
||||||
|
target_link_libraries(flutter INTERFACE |
||||||
|
PkgConfig::GTK |
||||||
|
PkgConfig::GLIB |
||||||
|
PkgConfig::GIO |
||||||
|
) |
||||||
|
add_dependencies(flutter flutter_assemble) |
||||||
|
|
||||||
|
# === Flutter tool backend === |
||||||
|
# _phony_ is a non-existent file to force this command to run every time, |
||||||
|
# since currently there's no way to get a full input/output list from the |
||||||
|
# flutter tool. |
||||||
|
add_custom_command( |
||||||
|
OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} |
||||||
|
${CMAKE_CURRENT_BINARY_DIR}/_phony_ |
||||||
|
COMMAND ${CMAKE_COMMAND} -E env |
||||||
|
${FLUTTER_TOOL_ENVIRONMENT} |
||||||
|
"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" |
||||||
|
${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} |
||||||
|
VERBATIM |
||||||
|
) |
||||||
|
add_custom_target(flutter_assemble DEPENDS |
||||||
|
"${FLUTTER_LIBRARY}" |
||||||
|
${FLUTTER_LIBRARY_HEADERS} |
||||||
|
) |
@ -0,0 +1,19 @@ |
|||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#include "generated_plugin_registrant.h" |
||||||
|
|
||||||
|
#include <embedder_texture/embedder_texture_plugin.h> |
||||||
|
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h> |
||||||
|
|
||||||
|
void fl_register_plugins(FlPluginRegistry* registry) { |
||||||
|
g_autoptr(FlPluginRegistrar) embedder_texture_registrar = |
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "EmbedderTexturePlugin"); |
||||||
|
embedder_texture_plugin_register_with_registrar(embedder_texture_registrar); |
||||||
|
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar = |
||||||
|
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin"); |
||||||
|
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar); |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
//
|
||||||
|
// Generated file. Do not edit.
|
||||||
|
//
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
|
||||||
|
#ifndef GENERATED_PLUGIN_REGISTRANT_ |
||||||
|
#define GENERATED_PLUGIN_REGISTRANT_ |
||||||
|
|
||||||
|
#include <flutter_linux/flutter_linux.h> |
||||||
|
|
||||||
|
// Registers Flutter plugins.
|
||||||
|
void fl_register_plugins(FlPluginRegistry* registry); |
||||||
|
|
||||||
|
#endif // GENERATED_PLUGIN_REGISTRANT_
|
@ -0,0 +1,25 @@ |
|||||||
|
# |
||||||
|
# Generated file, do not edit. |
||||||
|
# |
||||||
|
|
||||||
|
list(APPEND FLUTTER_PLUGIN_LIST |
||||||
|
embedder_texture |
||||||
|
flutter_secure_storage_linux |
||||||
|
) |
||||||
|
|
||||||
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST |
||||||
|
) |
||||||
|
|
||||||
|
set(PLUGIN_BUNDLED_LIBRARIES) |
||||||
|
|
||||||
|
foreach(plugin ${FLUTTER_PLUGIN_LIST}) |
||||||
|
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin}) |
||||||
|
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) |
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>) |
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) |
||||||
|
endforeach(plugin) |
||||||
|
|
||||||
|
foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) |
||||||
|
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin}) |
||||||
|
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) |
||||||
|
endforeach(ffi_plugin) |
@ -0,0 +1,6 @@ |
|||||||
|
#include "my_application.h" |
||||||
|
|
||||||
|
int main(int argc, char** argv) { |
||||||
|
g_autoptr(MyApplication) app = my_application_new(); |
||||||
|
return g_application_run(G_APPLICATION(app), argc, argv); |
||||||
|
} |
@ -0,0 +1,104 @@ |
|||||||
|
#include "my_application.h" |
||||||
|
|
||||||
|
#include <flutter_linux/flutter_linux.h> |
||||||
|
#ifdef GDK_WINDOWING_X11 |
||||||
|
#include <gdk/gdkx.h> |
||||||
|
#endif |
||||||
|
|
||||||
|
#include "flutter/generated_plugin_registrant.h" |
||||||
|
|
||||||
|
struct _MyApplication { |
||||||
|
GtkApplication parent_instance; |
||||||
|
char** dart_entrypoint_arguments; |
||||||
|
}; |
||||||
|
|
||||||
|
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) |
||||||
|
|
||||||
|
// Implements GApplication::activate.
|
||||||
|
static void my_application_activate(GApplication* application) { |
||||||
|
MyApplication* self = MY_APPLICATION(application); |
||||||
|
GtkWindow* window = |
||||||
|
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); |
||||||
|
|
||||||
|
// Use a header bar when running in GNOME as this is the common style used
|
||||||
|
// by applications and is the setup most users will be using (e.g. Ubuntu
|
||||||
|
// desktop).
|
||||||
|
// If running on X and not using GNOME then just use a traditional title bar
|
||||||
|
// in case the window manager does more exotic layout, e.g. tiling.
|
||||||
|
// If running on Wayland assume the header bar will work (may need changing
|
||||||
|
// if future cases occur).
|
||||||
|
gboolean use_header_bar = TRUE; |
||||||
|
#ifdef GDK_WINDOWING_X11 |
||||||
|
GdkScreen* screen = gtk_window_get_screen(window); |
||||||
|
if (GDK_IS_X11_SCREEN(screen)) { |
||||||
|
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); |
||||||
|
if (g_strcmp0(wm_name, "GNOME Shell") != 0) { |
||||||
|
use_header_bar = FALSE; |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
if (use_header_bar) { |
||||||
|
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); |
||||||
|
gtk_widget_show(GTK_WIDGET(header_bar)); |
||||||
|
gtk_header_bar_set_title(header_bar, "flutter_example_packages"); |
||||||
|
gtk_header_bar_set_show_close_button(header_bar, TRUE); |
||||||
|
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); |
||||||
|
} else { |
||||||
|
gtk_window_set_title(window, "flutter_example_packages"); |
||||||
|
} |
||||||
|
|
||||||
|
gtk_window_set_default_size(window, 1280, 720); |
||||||
|
gtk_widget_show(GTK_WIDGET(window)); |
||||||
|
|
||||||
|
g_autoptr(FlDartProject) project = fl_dart_project_new(); |
||||||
|
fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); |
||||||
|
|
||||||
|
FlView* view = fl_view_new(project); |
||||||
|
gtk_widget_show(GTK_WIDGET(view)); |
||||||
|
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); |
||||||
|
|
||||||
|
fl_register_plugins(FL_PLUGIN_REGISTRY(view)); |
||||||
|
|
||||||
|
gtk_widget_grab_focus(GTK_WIDGET(view)); |
||||||
|
} |
||||||
|
|
||||||
|
// Implements GApplication::local_command_line.
|
||||||
|
static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { |
||||||
|
MyApplication* self = MY_APPLICATION(application); |
||||||
|
// Strip out the first argument as it is the binary name.
|
||||||
|
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); |
||||||
|
|
||||||
|
g_autoptr(GError) error = nullptr; |
||||||
|
if (!g_application_register(application, nullptr, &error)) { |
||||||
|
g_warning("Failed to register: %s", error->message); |
||||||
|
*exit_status = 1; |
||||||
|
return TRUE; |
||||||
|
} |
||||||
|
|
||||||
|
g_application_activate(application); |
||||||
|
*exit_status = 0; |
||||||
|
|
||||||
|
return TRUE; |
||||||
|
} |
||||||
|
|
||||||
|
// Implements GObject::dispose.
|
||||||
|
static void my_application_dispose(GObject* object) { |
||||||
|
MyApplication* self = MY_APPLICATION(object); |
||||||
|
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); |
||||||
|
G_OBJECT_CLASS(my_application_parent_class)->dispose(object); |
||||||
|
} |
||||||
|
|
||||||
|
static void my_application_class_init(MyApplicationClass* klass) { |
||||||
|
G_APPLICATION_CLASS(klass)->activate = my_application_activate; |
||||||
|
G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; |
||||||
|
G_OBJECT_CLASS(klass)->dispose = my_application_dispose; |
||||||
|
} |
||||||
|
|
||||||
|
static void my_application_init(MyApplication* self) {} |
||||||
|
|
||||||
|
MyApplication* my_application_new() { |
||||||
|
return MY_APPLICATION(g_object_new(my_application_get_type(), |
||||||
|
"application-id", APPLICATION_ID, |
||||||
|
"flags", G_APPLICATION_NON_UNIQUE, |
||||||
|
nullptr)); |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
#ifndef FLUTTER_MY_APPLICATION_H_ |
||||||
|
#define FLUTTER_MY_APPLICATION_H_ |
||||||
|
|
||||||
|
#include <gtk/gtk.h> |
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, |
||||||
|
GtkApplication) |
||||||
|
|
||||||
|
/**
|
||||||
|
* my_application_new: |
||||||
|
* |
||||||
|
* Creates a new Flutter-based application. |
||||||
|
* |
||||||
|
* Returns: a new #MyApplication. |
||||||
|
*/ |
||||||
|
MyApplication* my_application_new(); |
||||||
|
|
||||||
|
#endif // FLUTTER_MY_APPLICATION_H_
|
@ -0,0 +1,30 @@ |
|||||||
|
# Miscellaneous |
||||||
|
*.class |
||||||
|
*.log |
||||||
|
*.pyc |
||||||
|
*.swp |
||||||
|
.DS_Store |
||||||
|
.atom/ |
||||||
|
.buildlog/ |
||||||
|
.history |
||||||
|
.svn/ |
||||||
|
migrate_working_dir/ |
||||||
|
|
||||||
|
# IntelliJ related |
||||||
|
*.iml |
||||||
|
*.ipr |
||||||
|
*.iws |
||||||
|
.idea/ |
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in |
||||||
|
# VS Code which you may wish to be included in version control, so this line |
||||||
|
# is commented out by default. |
||||||
|
.vscode/ |
||||||
|
|
||||||
|
# Flutter/Dart/Pub related |
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. |
||||||
|
/pubspec.lock |
||||||
|
**/doc/api/ |
||||||
|
.dart_tool/ |
||||||
|
.packages |
||||||
|
build/ |
@ -0,0 +1 @@ |
|||||||
|
include: package:flutter_lints/flutter.yaml |
@ -0,0 +1,36 @@ |
|||||||
|
# SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
# SPDX-License-Identifier: BSD-3-Clause |
||||||
|
|
||||||
|
cmake_minimum_required(VERSION 3.10) |
||||||
|
|
||||||
|
set(PROJECT_NAME embedder_texture) |
||||||
|
set(PLUGIN_NAME embedder_texture_platform_plugin) |
||||||
|
|
||||||
|
project(${PROJECT_NAME} LANGUAGES CXX) |
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD 17) |
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON) |
||||||
|
|
||||||
|
set(CMAKE_CXX_FLAGS "-Wall -Wextra -Wno-psabi") |
||||||
|
set(CMAKE_CXX_FLAGS_RELEASE "-O3") |
||||||
|
|
||||||
|
find_package(PkgConfig REQUIRED) |
||||||
|
find_package(Qt5 COMPONENTS Core Multimedia REQUIRED) |
||||||
|
|
||||||
|
pkg_check_modules(FlutterEmbedder REQUIRED IMPORTED_TARGET flutter-embedder) |
||||||
|
pkg_check_modules(GLES REQUIRED IMPORTED_TARGET glesv2) |
||||||
|
pkg_check_modules(SC REQUIRED IMPORTED_TARGET streamcamera) |
||||||
|
|
||||||
|
add_library(${PLUGIN_NAME} SHARED |
||||||
|
embedder_texture_plugin.cpp |
||||||
|
camera_pixels_helper.cpp |
||||||
|
camera_egl_helper.cpp |
||||||
|
camera.cpp |
||||||
|
) |
||||||
|
|
||||||
|
set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) |
||||||
|
target_link_libraries(${PLUGIN_NAME} PRIVATE PkgConfig::FlutterEmbedder PkgConfig::GLES PkgConfig::SC) |
||||||
|
target_link_libraries(${PLUGIN_NAME} PUBLIC Qt5::Core Qt5::Multimedia) |
||||||
|
|
||||||
|
target_include_directories(${PLUGIN_NAME} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include) |
||||||
|
target_compile_definitions(${PLUGIN_NAME} PRIVATE PLUGIN_IMPL) |
@ -0,0 +1,114 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#include <embedder_texture/camera.h> |
||||||
|
#include <embedder_texture/camera_egl_helper.h> |
||||||
|
#include <embedder_texture/camera_pixels_helper.h> |
||||||
|
|
||||||
|
Camera::Camera(TextureRegistrar *plugin) |
||||||
|
: m_plugin(plugin) |
||||||
|
, m_manager(StreamCameraManager()) |
||||||
|
{ |
||||||
|
CameraEGLHelper::EGLInit(); |
||||||
|
} |
||||||
|
|
||||||
|
void Camera::InitializeCamera(int cameraID) |
||||||
|
{ |
||||||
|
if (m_cameraId != cameraID && m_manager->getNumberOfCameras()) { |
||||||
|
Aurora::StreamCamera::CameraInfo info; |
||||||
|
|
||||||
|
if (m_manager->getCameraInfo(cameraID, info)) { |
||||||
|
m_cameraId = cameraID; |
||||||
|
m_camera = m_manager->openCamera(info.id); |
||||||
|
m_camera->setListener(this); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
Aurora::StreamCamera::CameraCapability Camera::StartCapture() |
||||||
|
{ |
||||||
|
if (m_camera) { |
||||||
|
Aurora::StreamCamera::CameraInfo info; |
||||||
|
|
||||||
|
if (m_camera->getInfo(info)) { |
||||||
|
std::vector<Aurora::StreamCamera::CameraCapability> caps; |
||||||
|
if (m_manager->queryCapabilities(info.id, caps)) { |
||||||
|
for(Aurora::StreamCamera::CameraCapability cap : caps) { |
||||||
|
if (m_widthTexture + m_heightTexture < cap.width + cap.height) { |
||||||
|
m_camera->startCapture(cap); |
||||||
|
return cap; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return Aurora::StreamCamera::CameraCapability{}; |
||||||
|
} |
||||||
|
|
||||||
|
void Camera::StopCapture() |
||||||
|
{ |
||||||
|
if (m_camera) { |
||||||
|
m_cameraId = -1; |
||||||
|
m_buffer = nullptr; |
||||||
|
m_camera->stopCapture(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
std::map<Encodable, Encodable> Camera::Register(int cameraID, int width, int height) |
||||||
|
{ |
||||||
|
m_widthTexture = width; |
||||||
|
m_heightTexture = height; |
||||||
|
|
||||||
|
m_textureId = m_plugin->RegisterTexture( |
||||||
|
[this](size_t width, size_t height) { return this->m_buffer; }); |
||||||
|
|
||||||
|
InitializeCamera(cameraID); |
||||||
|
auto cab = StartCapture(); |
||||||
|
|
||||||
|
return std::map<Encodable, Encodable>{ |
||||||
|
{"textureId", m_textureId}, |
||||||
|
{"width", cab.width}, |
||||||
|
{"height", cab.height}, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
void Camera::Unregister() |
||||||
|
{ |
||||||
|
StopCapture(); |
||||||
|
m_plugin->UnregisterTexture(m_textureId); |
||||||
|
} |
||||||
|
|
||||||
|
void Camera::onCameraFrame(std::shared_ptr<Aurora::StreamCamera::GraphicBuffer> buffer) |
||||||
|
{ |
||||||
|
if (buffer->handleType == Aurora::StreamCamera::HandleType::EGL) { |
||||||
|
// @todo Not tested. The device needs to be completed.
|
||||||
|
auto eglImage = CameraEGLHelper::EGLCreateImage(buffer); |
||||||
|
this->m_buffer = new TextureVariant(FlutterEGLImage{ |
||||||
|
eglImage, |
||||||
|
buffer->width, |
||||||
|
buffer->height, |
||||||
|
}); |
||||||
|
} else { |
||||||
|
auto pixels = CameraPixelsHelper::YUVtoARGB(buffer->mapYCbCr()); |
||||||
|
this->m_buffer = new TextureVariant(FlutterPixelBuffer{ |
||||||
|
pixels, |
||||||
|
buffer->width, |
||||||
|
buffer->height, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
m_plugin->MarkTextureAvailable(m_textureId); |
||||||
|
} |
||||||
|
|
||||||
|
void Camera::onCameraError(const std::string &errorDescription) |
||||||
|
{ |
||||||
|
this->Unregister(); |
||||||
|
std::cout << errorDescription << std::endl; |
||||||
|
} |
||||||
|
|
||||||
|
void Camera::onCameraParameterChanged(Aurora::StreamCamera::CameraParameter parameter, |
||||||
|
const std::string &value) |
||||||
|
{ |
||||||
|
std::cout << "onCameraParameterChanged" << std::endl; |
||||||
|
} |
@ -0,0 +1,42 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#include <embedder_texture/camera_egl_helper.h> |
||||||
|
#include <flutter/platform-methods.h> |
||||||
|
|
||||||
|
#include <GLES2/gl2.h> |
||||||
|
#include <GLES2/gl2ext.h> |
||||||
|
|
||||||
|
static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; |
||||||
|
static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; |
||||||
|
|
||||||
|
void CameraEGLHelper::EGLInit() |
||||||
|
{ |
||||||
|
eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>( |
||||||
|
eglGetProcAddress("eglCreateImageKHR")); |
||||||
|
eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>( |
||||||
|
eglGetProcAddress("eglDestroyImageKHR")); |
||||||
|
} |
||||||
|
|
||||||
|
EGLImageKHR CameraEGLHelper::EGLCreateImage( |
||||||
|
std::shared_ptr<Aurora::StreamCamera::GraphicBuffer> buffer) |
||||||
|
{ |
||||||
|
auto display = PlatformMethods::GetEGLDisplay(); |
||||||
|
auto context = PlatformMethods::GetEGLContext(); |
||||||
|
|
||||||
|
const void *handle = buffer->handle; |
||||||
|
GLint eglImgAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE}; |
||||||
|
return eglCreateImageKHR(display, |
||||||
|
context, |
||||||
|
EGL_NATIVE_BUFFER_ANDROID, |
||||||
|
(EGLClientBuffer) handle, |
||||||
|
eglImgAttrs); |
||||||
|
} |
||||||
|
|
||||||
|
void CameraEGLHelper::EGLDestroyImage(EGLImageKHR image) |
||||||
|
{ |
||||||
|
auto display = PlatformMethods::GetEGLDisplay(); |
||||||
|
|
||||||
|
eglDestroyImageKHR(display, image); |
||||||
|
} |
@ -0,0 +1,106 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#include <embedder_texture/camera_helper.h> |
||||||
|
|
||||||
|
static PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; |
||||||
|
static PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; |
||||||
|
|
||||||
|
void CameraHelper::EGLInit() |
||||||
|
{ |
||||||
|
eglCreateImageKHR = reinterpret_cast<PFNEGLCREATEIMAGEKHRPROC>( |
||||||
|
eglGetProcAddress("eglCreateImageKHR")); |
||||||
|
eglDestroyImageKHR = reinterpret_cast<PFNEGLDESTROYIMAGEKHRPROC>( |
||||||
|
eglGetProcAddress("eglDestroyImageKHR")); |
||||||
|
} |
||||||
|
|
||||||
|
EGLImageKHR CameraHelper::EGLCreateImage( |
||||||
|
std::shared_ptr<const Aurora::StreamCamera::YCbCrFrame> frame) |
||||||
|
{ |
||||||
|
auto eglDisplay = PlatformMethods::GetEGLDisplay(); |
||||||
|
auto eglContext = PlatformMethods::GetEGLContext(); |
||||||
|
|
||||||
|
const void *handle = buffer->handle; |
||||||
|
GLint eglImgAttrs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE, EGL_NONE}; |
||||||
|
return s_eglCreateImageKHR(eglDisplay, |
||||||
|
eglContext, |
||||||
|
EGL_NATIVE_BUFFER_ANDROID, |
||||||
|
(EGLClientBuffer) handle, |
||||||
|
eglImgAttrs); |
||||||
|
} |
||||||
|
|
||||||
|
void CameraHelper::EGLDestroyImage(EGLImageKHR eglImage, EGLDisplay eglDisplay) |
||||||
|
{ |
||||||
|
return eglDestroyImageKHR(eglDisplay, eglImage); |
||||||
|
} |
||||||
|
|
||||||
|
uint8_t *CameraHelper::YUVtoARGB(std::shared_ptr<const Aurora::StreamCamera::YCbCrFrame> frame) |
||||||
|
{ |
||||||
|
QSize size(frame->width, frame->height); |
||||||
|
QImage *image = new QImage(size, QImage::Format_ARGB32); |
||||||
|
|
||||||
|
planarYuv420ToArgb(frame->y, |
||||||
|
frame->cb, |
||||||
|
frame->cr, |
||||||
|
frame->yStride, |
||||||
|
frame->cStride, |
||||||
|
frame->cStride, |
||||||
|
frame->chromaStep, |
||||||
|
reinterpret_cast<quint32 *>(image->bits()), |
||||||
|
frame->width, |
||||||
|
frame->height); |
||||||
|
|
||||||
|
return static_cast<uint8_t *>(image->bits()); |
||||||
|
} |
||||||
|
|
||||||
|
quint32 CameraHelper::yuvToArgb(qint32 y, qint32 rv, qint32 guv, qint32 bu, qint32 a = 255) |
||||||
|
{ |
||||||
|
qint32 yy = (y - 16) * 298; |
||||||
|
|
||||||
|
return (a << 24) | qBound(0, (yy + rv) >> 8, 255) << 16 | qBound(0, (yy - guv) >> 8, 255) << 8 |
||||||
|
| qBound(0, (yy + bu) >> 8, 255); |
||||||
|
} |
||||||
|
|
||||||
|
void CameraHelper::planarYuv420ToArgb(const uchar *y, |
||||||
|
const uchar *u, |
||||||
|
const uchar *v, |
||||||
|
qint32 yStride, |
||||||
|
qint32 uStride, |
||||||
|
qint32 vStride, |
||||||
|
qint32 uvPixelStride, |
||||||
|
quint32 *rgb, |
||||||
|
qint32 width, |
||||||
|
qint32 height) |
||||||
|
{ |
||||||
|
quint32 *rgb0 = rgb; |
||||||
|
quint32 *rgb1 = rgb + width; |
||||||
|
|
||||||
|
for (qint32 j = 0; j < height; j += 2) { |
||||||
|
const uchar *lineY0 = y; |
||||||
|
const uchar *lineY1 = y + yStride; |
||||||
|
const uchar *lineU = u; |
||||||
|
const uchar *lineV = v; |
||||||
|
|
||||||
|
for (qint32 i = 0; i < width; i += 2) { |
||||||
|
const qint32 uu = *lineU - 128; |
||||||
|
const qint32 vv = *lineV - 128; |
||||||
|
const qint32 rv = 409 * vv + 128; |
||||||
|
const qint32 guv = 100 * uu + 208 * vv + 128; |
||||||
|
const qint32 bu = 516 * uu + 128; |
||||||
|
|
||||||
|
lineU += uvPixelStride; |
||||||
|
lineV += uvPixelStride; |
||||||
|
*rgb0++ = yuvToArgb(*lineY0++, rv, guv, bu); |
||||||
|
*rgb0++ = yuvToArgb(*lineY0++, rv, guv, bu); |
||||||
|
*rgb1++ = yuvToArgb(*lineY1++, rv, guv, bu); |
||||||
|
*rgb1++ = yuvToArgb(*lineY1++, rv, guv, bu); |
||||||
|
} |
||||||
|
|
||||||
|
y += yStride << 1; |
||||||
|
u += uStride; |
||||||
|
v += vStride; |
||||||
|
rgb0 += width; |
||||||
|
rgb1 += width; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,77 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#include <embedder_texture/camera_pixels_helper.h> |
||||||
|
|
||||||
|
uint8_t *CameraPixelsHelper::YUVtoARGB(std::shared_ptr<const Aurora::StreamCamera::YCbCrFrame> frame) |
||||||
|
{ |
||||||
|
QSize size(frame->width, frame->height); |
||||||
|
QImage *image = new QImage(size, QImage::Format_ARGB32); |
||||||
|
|
||||||
|
planarYuv420ToArgb(frame->y, |
||||||
|
frame->cr, |
||||||
|
frame->cb, |
||||||
|
frame->yStride, |
||||||
|
frame->cStride, |
||||||
|
frame->cStride, |
||||||
|
frame->chromaStep, |
||||||
|
reinterpret_cast<quint32 *>(image->bits()), |
||||||
|
frame->width, |
||||||
|
frame->height); |
||||||
|
|
||||||
|
return static_cast<uint8_t *>(image->bits()); |
||||||
|
} |
||||||
|
|
||||||
|
quint32 CameraPixelsHelper::yuvToArgb(qint32 y, qint32 rv, qint32 guv, qint32 bu, qint32 a = 255) |
||||||
|
{ |
||||||
|
qint32 yy = (y - 16) * 298; |
||||||
|
|
||||||
|
return (a << 24) |
||||||
|
| qBound(0, (yy + rv) >> 8, 255) << 16 |
||||||
|
| qBound(0, (yy - guv) >> 8, 255) << 8 |
||||||
|
| qBound(0, (yy + bu) >> 8, 255); |
||||||
|
} |
||||||
|
|
||||||
|
void CameraPixelsHelper::planarYuv420ToArgb(const uchar *y, |
||||||
|
const uchar *u, |
||||||
|
const uchar *v, |
||||||
|
qint32 yStride, |
||||||
|
qint32 uStride, |
||||||
|
qint32 vStride, |
||||||
|
qint32 uvPixelStride, |
||||||
|
quint32 *rgb, |
||||||
|
qint32 width, |
||||||
|
qint32 height) |
||||||
|
{ |
||||||
|
quint32 *rgb0 = rgb; |
||||||
|
quint32 *rgb1 = rgb + width; |
||||||
|
|
||||||
|
for (qint32 j = 0; j < height; j += 2) { |
||||||
|
const uchar *lineY0 = y; |
||||||
|
const uchar *lineY1 = y + yStride; |
||||||
|
const uchar *lineU = u; |
||||||
|
const uchar *lineV = v; |
||||||
|
|
||||||
|
for (qint32 i = 0; i < width; i += 2) { |
||||||
|
const qint32 uu = *lineU - 128; |
||||||
|
const qint32 vv = *lineV - 128; |
||||||
|
const qint32 rv = 409 * vv + 128; |
||||||
|
const qint32 guv = 100 * uu + 208 * vv + 128; |
||||||
|
const qint32 bu = 516 * uu + 128; |
||||||
|
|
||||||
|
lineU += uvPixelStride; |
||||||
|
lineV += uvPixelStride; |
||||||
|
*rgb0++ = yuvToArgb(*lineY0++, rv, guv, bu); |
||||||
|
*rgb0++ = yuvToArgb(*lineY0++, rv, guv, bu); |
||||||
|
*rgb1++ = yuvToArgb(*lineY1++, rv, guv, bu); |
||||||
|
*rgb1++ = yuvToArgb(*lineY1++, rv, guv, bu); |
||||||
|
} |
||||||
|
|
||||||
|
y += yStride << 1; |
||||||
|
u += uStride; |
||||||
|
v += vStride; |
||||||
|
rgb0 += width; |
||||||
|
rgb1 += width; |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,87 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#include <embedder_texture/embedder_texture_plugin.h> |
||||||
|
#include <flutter/method-channel.h> |
||||||
|
#include <flutter/platform-data.h> |
||||||
|
#include <flutter/platform-events.h> |
||||||
|
#include <flutter/platform-methods.h> |
||||||
|
|
||||||
|
EmbedderTexturePlugin::EmbedderTexturePlugin() |
||||||
|
{ |
||||||
|
PlatformEvents::SubscribeOrientationChanged([this](DisplayRotation orientation) { |
||||||
|
if (this->m_isEnableOrientationChanged) { |
||||||
|
EventChannel("embedder_texture_orientation", MethodCodecType::Standard) |
||||||
|
.SendEvent(static_cast<int>(orientation)); |
||||||
|
} |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
void EmbedderTexturePlugin::RegisterWithRegistrar(PluginRegistrar ®istrar) |
||||||
|
{ |
||||||
|
TextureRegistrar *plugin = registrar.GetRegisterTexture(); |
||||||
|
|
||||||
|
m_camera = new Camera(plugin); |
||||||
|
|
||||||
|
registrar.RegisterMethodChannel("embedder_texture", |
||||||
|
MethodCodecType::Standard, |
||||||
|
[this](const MethodCall &call) { this->onMethodCall(call); }); |
||||||
|
|
||||||
|
registrar.RegisterEventChannel( |
||||||
|
"embedder_texture_orientation", |
||||||
|
MethodCodecType::Standard, |
||||||
|
[this](const Encodable &) { |
||||||
|
this->m_isEnableOrientationChanged = true; |
||||||
|
return EventResponse(); |
||||||
|
}, |
||||||
|
[this](const Encodable &) { |
||||||
|
this->m_isEnableOrientationChanged = false; |
||||||
|
return EventResponse(); |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
void EmbedderTexturePlugin::onMethodCall(const MethodCall &call) |
||||||
|
{ |
||||||
|
const auto &method = call.GetMethod(); |
||||||
|
|
||||||
|
if (method == "create") { |
||||||
|
onCreate(call); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (method == "remove") { |
||||||
|
onRemove(call); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
unimplemented(call); |
||||||
|
} |
||||||
|
|
||||||
|
void EmbedderTexturePlugin::onCreate(const MethodCall &call) |
||||||
|
{ |
||||||
|
auto cameraId = 0; |
||||||
|
auto width = call.GetArgument<Encodable::Int>("width"); |
||||||
|
auto height = call.GetArgument<Encodable::Int>("height"); |
||||||
|
|
||||||
|
auto result = m_camera->Register(cameraId, width, height); |
||||||
|
|
||||||
|
result["orientation"] = static_cast<int>(PlatformMethods::GetOrientation()); |
||||||
|
|
||||||
|
call.SendSuccessResponse(result); |
||||||
|
} |
||||||
|
|
||||||
|
void EmbedderTexturePlugin::onRemove(const MethodCall &call) |
||||||
|
{ |
||||||
|
auto textureId = call.GetArgument<Encodable::Int>("textureId"); |
||||||
|
|
||||||
|
m_camera->Unregister(); |
||||||
|
|
||||||
|
call.SendSuccessResponse(true); |
||||||
|
} |
||||||
|
|
||||||
|
void EmbedderTexturePlugin::unimplemented(const MethodCall &call) |
||||||
|
{ |
||||||
|
call.SendSuccessResponse(nullptr); |
||||||
|
} |
@ -0,0 +1,47 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#ifndef CAMERA_BUFFER_H |
||||||
|
#define CAMERA_BUFFER_H |
||||||
|
|
||||||
|
#include <flutter/plugin-interface.h> |
||||||
|
#include <streamcamera/streamcamera.h> |
||||||
|
|
||||||
|
#include <QImage> |
||||||
|
#include <QtCore> |
||||||
|
|
||||||
|
class Camera : public Aurora::StreamCamera::CameraListener |
||||||
|
{ |
||||||
|
public: |
||||||
|
Camera(TextureRegistrar *plugin); |
||||||
|
|
||||||
|
void onCameraError(const std::string &errorDescription) override; |
||||||
|
void onCameraFrame(std::shared_ptr<Aurora::StreamCamera::GraphicBuffer> buffer) override; |
||||||
|
void onCameraParameterChanged(Aurora::StreamCamera::CameraParameter, |
||||||
|
const std::string &value) override; |
||||||
|
|
||||||
|
std::map<Encodable, Encodable> Register(int cameraID, int width, int height); |
||||||
|
void Unregister(); |
||||||
|
|
||||||
|
private: |
||||||
|
void InitializeCamera(int cameraID); |
||||||
|
Aurora::StreamCamera::CameraCapability StartCapture(); |
||||||
|
void StopCapture(); |
||||||
|
|
||||||
|
private: |
||||||
|
TextureRegistrar *m_plugin; |
||||||
|
|
||||||
|
Aurora::StreamCamera::CameraManager *m_manager; |
||||||
|
std::shared_ptr<Aurora::StreamCamera::Camera> m_camera; |
||||||
|
|
||||||
|
int m_cameraId = -1; |
||||||
|
int64_t m_textureId; |
||||||
|
|
||||||
|
int m_widthTexture; |
||||||
|
int m_heightTexture; |
||||||
|
|
||||||
|
TextureVariant *m_buffer; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif /* CAMERA_BUFFER_H */ |
@ -0,0 +1,21 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#ifndef CAMERA_EGL_HELPER_H |
||||||
|
#define CAMERA_EGL_HELPER_H |
||||||
|
|
||||||
|
#include <streamcamera/streamcamera.h> |
||||||
|
|
||||||
|
#include <EGL/egl.h> |
||||||
|
#include <EGL/eglext.h> |
||||||
|
|
||||||
|
class CameraEGLHelper |
||||||
|
{ |
||||||
|
public: |
||||||
|
static void EGLInit(); |
||||||
|
static EGLImageKHR EGLCreateImage(std::shared_ptr<Aurora::StreamCamera::GraphicBuffer> buffer); |
||||||
|
static void EGLDestroyImage(EGLImageKHR image); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif /* CAMERA_EGL_HELPER_H */ |
@ -0,0 +1,33 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#ifndef CAMERA_PIXELS_HELPER_H |
||||||
|
#define CAMERA_PIXELS_HELPER_H |
||||||
|
|
||||||
|
#include <flutter/plugin-interface.h> |
||||||
|
#include <streamcamera/streamcamera.h> |
||||||
|
|
||||||
|
#include <QImage> |
||||||
|
#include <QtCore> |
||||||
|
|
||||||
|
class CameraPixelsHelper |
||||||
|
{ |
||||||
|
public: |
||||||
|
static uint8_t *YUVtoARGB(std::shared_ptr<const Aurora::StreamCamera::YCbCrFrame> frame); |
||||||
|
|
||||||
|
private: |
||||||
|
static quint32 yuvToArgb(qint32 y, qint32 rv, qint32 guv, qint32 bu, qint32 a); |
||||||
|
static void planarYuv420ToArgb(const uchar *y, |
||||||
|
const uchar *u, |
||||||
|
const uchar *v, |
||||||
|
qint32 yStride, |
||||||
|
qint32 uStride, |
||||||
|
qint32 vStride, |
||||||
|
qint32 uvPixelStride, |
||||||
|
quint32 *rgb, |
||||||
|
qint32 width, |
||||||
|
qint32 height); |
||||||
|
}; |
||||||
|
|
||||||
|
#endif /* CAMERA_PIXELS_HELPER_H */ |
@ -0,0 +1,35 @@ |
|||||||
|
/**
|
||||||
|
* SPDX-FileCopyrightText: Copyright 2023 Open Mobile Platform LLC <community@omp.ru> |
||||||
|
* SPDX-License-Identifier: BSD-3-Clause |
||||||
|
*/ |
||||||
|
#ifndef EMBEDDER_TEXTURE_PLUGIN_H |
||||||
|
#define EMBEDDER_TEXTURE_PLUGIN_H |
||||||
|
|
||||||
|
#include <flutter/plugin-interface.h> |
||||||
|
|
||||||
|
#include <embedder_texture/camera.h> |
||||||
|
|
||||||
|
#ifdef PLUGIN_IMPL |
||||||
|
#define PLUGIN_EXPORT __attribute__((visibility("default"))) |
||||||
|
#else |
||||||
|
#define PLUGIN_EXPORT |
||||||
|
#endif |
||||||
|
|
||||||
|
class PLUGIN_EXPORT EmbedderTexturePlugin final : public PluginInterface |
||||||
|
{ |
||||||
|
public: |
||||||
|
EmbedderTexturePlugin(); |
||||||
|
void RegisterWithRegistrar(PluginRegistrar ®istrar) override; |
||||||
|
|
||||||
|
private: |
||||||
|
void onMethodCall(const MethodCall &call); |
||||||
|
void onCreate(const MethodCall &call); |
||||||
|
void onRemove(const MethodCall &call); |
||||||
|
void unimplemented(const MethodCall &call); |
||||||
|
|
||||||
|
private: |
||||||
|
Camera *m_camera; |
||||||
|
bool m_isEnableOrientationChanged = false; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif /* EMBEDDER_TEXTURE_PLUGIN_H */ |
@ -0,0 +1,162 @@ |
|||||||
|
import 'package:flutter/material.dart'; |
||||||
|
|
||||||
|
import 'embedder_texture_platform_interface.dart'; |
||||||
|
|
||||||
|
enum OrientationEvent { |
||||||
|
undefined, |
||||||
|
portrait, |
||||||
|
landscape, |
||||||
|
portraitFlipped, |
||||||
|
landscapeFlipped, |
||||||
|
} |
||||||
|
|
||||||
|
class EmbedderTexture extends StatefulWidget { |
||||||
|
const EmbedderTexture({ |
||||||
|
super.key, |
||||||
|
required this.width, |
||||||
|
required this.height, |
||||||
|
}); |
||||||
|
|
||||||
|
final double width; |
||||||
|
final double height; |
||||||
|
|
||||||
|
@override |
||||||
|
State<EmbedderTexture> createState() => _EmbedderTextureState(); |
||||||
|
} |
||||||
|
|
||||||
|
class _EmbedderTextureState extends State<EmbedderTexture> { |
||||||
|
int _textureID = 0; |
||||||
|
int _cameraWidth = 0; |
||||||
|
int _cameraHeight = 0; |
||||||
|
OrientationEvent _orientation = OrientationEvent.undefined; |
||||||
|
|
||||||
|
@override |
||||||
|
initState() { |
||||||
|
super.initState(); |
||||||
|
EmbedderTexturePlatform.instance |
||||||
|
.create(widget.width, widget.height) |
||||||
|
.then((data) => setState(() { |
||||||
|
if (mounted) { |
||||||
|
_textureID = data['textureId']!; |
||||||
|
_cameraWidth = data['width']!; |
||||||
|
_cameraHeight = data['height']!; |
||||||
|
|
||||||
|
switch (data['orientation']!) { |
||||||
|
case 0: |
||||||
|
_orientation = OrientationEvent.portrait; |
||||||
|
break; |
||||||
|
case 90: |
||||||
|
_orientation = OrientationEvent.landscape; |
||||||
|
break; |
||||||
|
case 180: |
||||||
|
_orientation = OrientationEvent.portraitFlipped; |
||||||
|
break; |
||||||
|
case 270: |
||||||
|
_orientation = OrientationEvent.landscapeFlipped; |
||||||
|
break; |
||||||
|
default: |
||||||
|
_orientation = OrientationEvent.undefined; |
||||||
|
} |
||||||
|
|
||||||
|
debugPrint(data.toString()); |
||||||
|
} |
||||||
|
})); |
||||||
|
|
||||||
|
EmbedderTexturePlatform.instance.onChangeOrientation().listen((event) { |
||||||
|
setState(() { |
||||||
|
if (mounted) { |
||||||
|
_orientation = event; |
||||||
|
} |
||||||
|
}); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
void dispose() { |
||||||
|
super.dispose(); |
||||||
|
EmbedderTexturePlatform.instance.remove(_textureID); |
||||||
|
} |
||||||
|
|
||||||
|
// @override |
||||||
|
// Widget build(BuildContext context) { |
||||||
|
// if (_textureID != 0) { |
||||||
|
// double w = 0; |
||||||
|
// double h = 0; |
||||||
|
|
||||||
|
// if (MediaQuery.of(context).orientation == Orientation.portrait) { |
||||||
|
// w = _cameraWidth * widget.height / (widget.width - 40); |
||||||
|
// h = widget.width - 40; |
||||||
|
// } else { |
||||||
|
// w = _cameraHeight.toDouble(); |
||||||
|
// h = widget.height - 40 - 56; |
||||||
|
// } |
||||||
|
|
||||||
|
// return Container( |
||||||
|
// color: Colors.black, |
||||||
|
// width: widget.width, |
||||||
|
// height: widget.height, |
||||||
|
// child: RotationTransition( |
||||||
|
// turns: AlwaysStoppedAnimation( |
||||||
|
// _orientation == OrientationEvent.portrait ? 90 / 360 : 0, |
||||||
|
// ), |
||||||
|
// child: SizedBox( |
||||||
|
// width: w, |
||||||
|
// height: h, |
||||||
|
// child: Texture(textureId: _textureID), |
||||||
|
// ), |
||||||
|
// ), |
||||||
|
// ); |
||||||
|
// } |
||||||
|
// return const SizedBox.shrink(); |
||||||
|
// } |
||||||
|
|
||||||
|
@override |
||||||
|
Widget build(BuildContext context) { |
||||||
|
if (_textureID != 0) { |
||||||
|
double w = 0; |
||||||
|
double h = 0; |
||||||
|
|
||||||
|
if (_orientation == OrientationEvent.portrait || |
||||||
|
_orientation == OrientationEvent.portraitFlipped) { |
||||||
|
w = _cameraWidth * widget.height / (widget.width - 40); |
||||||
|
h = widget.width - 40; |
||||||
|
} else { |
||||||
|
w = _cameraHeight.toDouble(); |
||||||
|
h = widget.height - 40 - 56; |
||||||
|
} |
||||||
|
|
||||||
|
AlwaysStoppedAnimation<double>? turns; |
||||||
|
|
||||||
|
switch (_orientation) { |
||||||
|
case OrientationEvent.portrait: |
||||||
|
turns = const AlwaysStoppedAnimation(90 / 360); |
||||||
|
break; |
||||||
|
case OrientationEvent.portraitFlipped: |
||||||
|
turns = const AlwaysStoppedAnimation(270 / 360); |
||||||
|
break; |
||||||
|
case OrientationEvent.landscapeFlipped: |
||||||
|
turns = const AlwaysStoppedAnimation(180 / 360); |
||||||
|
break; |
||||||
|
default: |
||||||
|
turns = const AlwaysStoppedAnimation(0); |
||||||
|
} |
||||||
|
|
||||||
|
return Container( |
||||||
|
color: Colors.black, |
||||||
|
width: widget.width, |
||||||
|
height: widget.height, |
||||||
|
child: Center( |
||||||
|
child: RotationTransition( |
||||||
|
turns: turns, |
||||||
|
child: SizedBox( |
||||||
|
width: w, |
||||||
|
height: h, |
||||||
|
child: Texture(textureId: _textureID), |
||||||
|
), |
||||||
|
), |
||||||
|
), |
||||||
|
); |
||||||
|
} |
||||||
|
return const SizedBox.shrink(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,54 @@ |
|||||||
|
import 'package:embedder_texture/embedder_texture.dart'; |
||||||
|
import 'package:flutter/foundation.dart'; |
||||||
|
import 'package:flutter/services.dart'; |
||||||
|
|
||||||
|
import 'embedder_texture_platform_interface.dart'; |
||||||
|
|
||||||
|
/// An implementation of [LinuxOpenglPlatform] that uses method channels. |
||||||
|
class MethodChannelEmbedderTexture extends EmbedderTexturePlatform { |
||||||
|
/// The method channel used to interact with the native platform. |
||||||
|
@visibleForTesting |
||||||
|
final methodChannel = const MethodChannel('embedder_texture'); |
||||||
|
|
||||||
|
@override |
||||||
|
Future<Map<dynamic, dynamic>> create(double width, double height) async { |
||||||
|
final data = |
||||||
|
await methodChannel.invokeMethod<Map<dynamic, dynamic>?>('create', { |
||||||
|
'width': width.round(), |
||||||
|
'height': height.round(), |
||||||
|
}); |
||||||
|
return data ?? {}; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Future<bool> remove(int textureId) async { |
||||||
|
return await methodChannel.invokeMethod<bool>('remove', { |
||||||
|
'textureId': textureId, |
||||||
|
}) ?? |
||||||
|
false; |
||||||
|
} |
||||||
|
|
||||||
|
@override |
||||||
|
Stream<OrientationEvent> onChangeOrientation() async* { |
||||||
|
await for (final orientation |
||||||
|
in const EventChannel('embedder_texture_orientation') |
||||||
|
.receiveBroadcastStream()) { |
||||||
|
switch (orientation) { |
||||||
|
case 0: |
||||||
|
yield OrientationEvent.portrait; |
||||||
|
break; |
||||||
|
case 90: |
||||||
|
yield OrientationEvent.landscape; |
||||||
|
break; |
||||||
|
case 180: |
||||||
|
yield OrientationEvent.portraitFlipped; |
||||||
|
break; |
||||||
|
case 270: |
||||||
|
yield OrientationEvent.landscapeFlipped; |
||||||
|
break; |
||||||
|
default: |
||||||
|
yield OrientationEvent.undefined; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
import 'package:embedder_texture/embedder_texture.dart'; |
||||||
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart'; |
||||||
|
|
||||||
|
import 'embedder_texture_method_channel.dart'; |
||||||
|
|
||||||
|
abstract class EmbedderTexturePlatform extends PlatformInterface { |
||||||
|
/// Constructs a LinuxOpenglPlatform. |
||||||
|
EmbedderTexturePlatform() : super(token: _token); |
||||||
|
|
||||||
|
static final Object _token = Object(); |
||||||
|
|
||||||
|
static EmbedderTexturePlatform _instance = MethodChannelEmbedderTexture(); |
||||||
|
|
||||||
|
/// The default instance of [LinuxOpenglPlatform] to use. |
||||||
|
/// |
||||||
|
/// Defaults to [MethodChannelLinuxOpengl]. |
||||||
|
static EmbedderTexturePlatform get instance => _instance; |
||||||
|
|
||||||
|
/// Platform-specific implementations should set this with their own |
||||||
|
/// platform-specific class that extends [LinuxOpenglPlatform] when |
||||||
|
/// they register themselves. |
||||||
|
static set instance(EmbedderTexturePlatform instance) { |
||||||
|
PlatformInterface.verifyToken(instance, _token); |
||||||
|
_instance = instance; |
||||||
|
} |
||||||
|
|
||||||
|
Stream<OrientationEvent> onChangeOrientation() { |
||||||
|
throw UnimplementedError('onChangeOrientation() has not been implemented.'); |
||||||
|
} |
||||||
|
|
||||||
|
Future<Map<dynamic, dynamic>> create(double width, double height) { |
||||||
|
throw UnimplementedError('create() has not been implemented.'); |
||||||
|
} |
||||||
|
|
||||||
|
Future<bool> remove(int textureId) { |
||||||
|
throw UnimplementedError('remove() has not been implemented.'); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,24 @@ |
|||||||
|
cmake_minimum_required(VERSION 3.10) |
||||||
|
|
||||||
|
set(PROJECT_NAME "embedder_texture") |
||||||
|
project(${PROJECT_NAME} LANGUAGES CXX) |
||||||
|
|
||||||
|
set(PLUGIN_NAME "embedder_texture_plugin") |
||||||
|
|
||||||
|
list(APPEND PLUGIN_SOURCES |
||||||
|
"fl_my_texture_gl.cc" |
||||||
|
"embedder_texture_plugin.cc" |
||||||
|
) |
||||||
|
|
||||||
|
add_library(${PLUGIN_NAME} SHARED |
||||||
|
${PLUGIN_SOURCES} |
||||||
|
) |
||||||
|
|
||||||
|
apply_standard_settings(${PLUGIN_NAME}) |
||||||
|
|
||||||
|
set_target_properties(${PLUGIN_NAME} PROPERTIES CXX_VISIBILITY_PRESET hidden) |
||||||
|
target_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL) |
||||||
|
|
||||||
|
target_include_directories(${PLUGIN_NAME} INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") |
||||||
|
target_link_libraries(${PLUGIN_NAME} PRIVATE flutter) |
||||||
|
target_link_libraries(${PLUGIN_NAME} PUBLIC GL) |
@ -0,0 +1,173 @@ |
|||||||
|
#include "include/embedder_texture/embedder_texture_plugin.h" |
||||||
|
#include "include/fl_my_texture_gl.h" |
||||||
|
|
||||||
|
#include <flutter_linux/flutter_linux.h> |
||||||
|
#include <flutter_linux/fl_view.h> |
||||||
|
#include <GLES2/gl2.h> |
||||||
|
|
||||||
|
#include "embedder_texture_plugin_private.h" |
||||||
|
|
||||||
|
#define EMBEDDER_TEXTURE_PLUGIN(obj) \ |
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST((obj), embedder_texture_plugin_get_type(), \
|
||||||
|
LinuxOpenglPlugin)) |
||||||
|
|
||||||
|
struct _LinuxOpenglPlugin |
||||||
|
{ |
||||||
|
GObject parent_instance; |
||||||
|
|
||||||
|
unsigned int width = 0; |
||||||
|
unsigned int height = 0; |
||||||
|
unsigned int texture_id = 0; |
||||||
|
|
||||||
|
FlTexture *texture = nullptr; |
||||||
|
FlTextureRegistrar *texture_registrar = nullptr; |
||||||
|
|
||||||
|
FlMyTextureGL *myTexture = nullptr; |
||||||
|
FlView *fl_view = nullptr; |
||||||
|
}; |
||||||
|
|
||||||
|
G_DEFINE_TYPE(LinuxOpenglPlugin, embedder_texture_plugin, g_object_get_type()) |
||||||
|
|
||||||
|
// Called when a method call is received from Flutter.
|
||||||
|
static void embedder_texture_plugin_handle_method_call( |
||||||
|
LinuxOpenglPlugin *self, |
||||||
|
FlMethodCall *method_call) |
||||||
|
{ |
||||||
|
|
||||||
|
g_autoptr(FlMethodResponse) response = nullptr; |
||||||
|
|
||||||
|
const gchar *method = fl_method_call_get_name(method_call); |
||||||
|
|
||||||
|
if (strcmp(method, "create") == 0) |
||||||
|
{ |
||||||
|
response = create(self, method_call); |
||||||
|
} |
||||||
|
else if (strcmp(method, "remove") == 0) |
||||||
|
{ |
||||||
|
response = remove(self, method_call); |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new()); |
||||||
|
} |
||||||
|
|
||||||
|
fl_method_call_respond(method_call, response, nullptr); |
||||||
|
} |
||||||
|
|
||||||
|
FlMethodResponse *create( |
||||||
|
LinuxOpenglPlugin *self, |
||||||
|
FlMethodCall *method_call) |
||||||
|
{ |
||||||
|
GError *error = NULL; |
||||||
|
FlValue *args = fl_method_call_get_args(method_call); |
||||||
|
FlValue *w = fl_value_lookup_string(args, "width"); |
||||||
|
FlValue *h = fl_value_lookup_string(args, "height"); |
||||||
|
|
||||||
|
if (w != nullptr) |
||||||
|
{ |
||||||
|
self->width = fl_value_get_float(w); |
||||||
|
} |
||||||
|
|
||||||
|
if (h != nullptr) |
||||||
|
{ |
||||||
|
self->height = fl_value_get_float(h); |
||||||
|
} |
||||||
|
|
||||||
|
if (self->width != 0 && self->height != 0) |
||||||
|
{ |
||||||
|
static GLfloat pixels[] = |
||||||
|
{ |
||||||
|
1, 0, 0, |
||||||
|
0, 1, 0, |
||||||
|
0, 0, 1, |
||||||
|
1, 1, 1 |
||||||
|
}; |
||||||
|
|
||||||
|
// create in open gl
|
||||||
|
glGenTextures(1, &self->texture_id); |
||||||
|
glBindTexture(GL_TEXTURE_2D, self->texture_id); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); |
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); |
||||||
|
|
||||||
|
// create
|
||||||
|
self->myTexture = fl_my_texture_gl_new(GL_TEXTURE_2D, self->texture_id, self->width, self->height); |
||||||
|
self->texture = FL_TEXTURE(self->myTexture); |
||||||
|
|
||||||
|
// reg
|
||||||
|
fl_texture_registrar_register_texture(self->texture_registrar, self->texture); |
||||||
|
|
||||||
|
// draw
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 2, 2, 0, GL_RGB, GL_FLOAT, pixels); |
||||||
|
|
||||||
|
// send id
|
||||||
|
g_autoptr(FlValue) result = fl_value_new_int(reinterpret_cast<int64_t>(self->texture)); |
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); |
||||||
|
} |
||||||
|
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new( |
||||||
|
"500", |
||||||
|
"Method create() called without passing width or height parameters!", |
||||||
|
nullptr)); |
||||||
|
} |
||||||
|
|
||||||
|
FlMethodResponse *remove( |
||||||
|
LinuxOpenglPlugin *self, |
||||||
|
FlMethodCall *method_call) |
||||||
|
{ |
||||||
|
FlValue *args = fl_method_call_get_args(method_call); |
||||||
|
FlValue *id = fl_value_lookup_string(args, "textureId"); |
||||||
|
|
||||||
|
if (id != nullptr) |
||||||
|
{ |
||||||
|
int textureId = fl_value_get_int(id); |
||||||
|
fl_texture_registrar_unregister_texture(self->texture_registrar, self->texture); |
||||||
|
g_autoptr(FlValue) result = fl_value_new_null(); |
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(result)); |
||||||
|
} |
||||||
|
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new( |
||||||
|
"500", |
||||||
|
"Method remove() called without passing textureId parameters!", |
||||||
|
nullptr)); |
||||||
|
} |
||||||
|
|
||||||
|
static void embedder_texture_plugin_dispose(GObject *object) |
||||||
|
{ |
||||||
|
G_OBJECT_CLASS(embedder_texture_plugin_parent_class)->dispose(object); |
||||||
|
} |
||||||
|
|
||||||
|
static void embedder_texture_plugin_class_init(LinuxOpenglPluginClass *klass) |
||||||
|
{ |
||||||
|
G_OBJECT_CLASS(klass)->dispose = embedder_texture_plugin_dispose; |
||||||
|
} |
||||||
|
|
||||||
|
static void embedder_texture_plugin_init(LinuxOpenglPlugin *self) {} |
||||||
|
|
||||||
|
static void method_call_cb(FlMethodChannel *channel, FlMethodCall *method_call, |
||||||
|
gpointer user_data) |
||||||
|
{ |
||||||
|
LinuxOpenglPlugin *plugin = EMBEDDER_TEXTURE_PLUGIN(user_data); |
||||||
|
embedder_texture_plugin_handle_method_call(plugin, method_call); |
||||||
|
} |
||||||
|
|
||||||
|
void embedder_texture_plugin_register_with_registrar(FlPluginRegistrar *registrar) |
||||||
|
{ |
||||||
|
LinuxOpenglPlugin *plugin = EMBEDDER_TEXTURE_PLUGIN( |
||||||
|
g_object_new(embedder_texture_plugin_get_type(), nullptr)); |
||||||
|
|
||||||
|
plugin->fl_view = fl_plugin_registrar_get_view(registrar); |
||||||
|
plugin->texture_registrar = fl_plugin_registrar_get_texture_registrar(registrar); |
||||||
|
|
||||||
|
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new(); |
||||||
|
g_autoptr(FlMethodChannel) channel = |
||||||
|
fl_method_channel_new(fl_plugin_registrar_get_messenger(registrar), |
||||||
|
"embedder_texture", |
||||||
|
FL_METHOD_CODEC(codec)); |
||||||
|
fl_method_channel_set_method_call_handler(channel, method_call_cb, |
||||||
|
g_object_ref(plugin), |
||||||
|
g_object_unref); |
||||||
|
|
||||||
|
g_object_unref(plugin); |
||||||
|
} |
@ -0,0 +1,7 @@ |
|||||||
|
#include <flutter_linux/flutter_linux.h> |
||||||
|
|
||||||
|
#include "include/embedder_texture/embedder_texture_plugin.h" |
||||||
|
|
||||||
|
FlMethodResponse *create(LinuxOpenglPlugin *self, FlMethodCall *method_call); |
||||||
|
|
||||||
|
FlMethodResponse *remove(LinuxOpenglPlugin *self, FlMethodCall *method_call); |
@ -0,0 +1,44 @@ |
|||||||
|
#include "include/fl_my_texture_gl.h" |
||||||
|
|
||||||
|
G_DEFINE_TYPE(FlMyTextureGL, |
||||||
|
fl_my_texture_gl, |
||||||
|
fl_texture_gl_get_type()) |
||||||
|
|
||||||
|
static gboolean fl_my_texture_gl_populate(FlTextureGL *texture, |
||||||
|
uint32_t *target, |
||||||
|
uint32_t *name, |
||||||
|
uint32_t *width, |
||||||
|
uint32_t *height, |
||||||
|
GError **error) |
||||||
|
{ |
||||||
|
FlMyTextureGL* f = (FlMyTextureGL*) texture; |
||||||
|
*target = f->target; |
||||||
|
*name = f->name; |
||||||
|
*width = f->width; |
||||||
|
*height = f->height; |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
FlMyTextureGL *fl_my_texture_gl_new(uint32_t target, |
||||||
|
uint32_t name, |
||||||
|
uint32_t width, |
||||||
|
uint32_t height) |
||||||
|
{ |
||||||
|
auto r = FL_MY_TEXTURE_GL(g_object_new(fl_my_texture_gl_get_type(), nullptr)); |
||||||
|
r->target = target; |
||||||
|
r->name = name; |
||||||
|
r->width = width; |
||||||
|
r->height = height; |
||||||
|
return r; |
||||||
|
} |
||||||
|
|
||||||
|
static void fl_my_texture_gl_class_init( |
||||||
|
FlMyTextureGLClass *klass) |
||||||
|
{ |
||||||
|
FL_TEXTURE_GL_CLASS(klass)->populate = |
||||||
|
fl_my_texture_gl_populate; |
||||||
|
} |
||||||
|
|
||||||
|
static void fl_my_texture_gl_init(FlMyTextureGL *self) |
||||||
|
{ |
||||||
|
} |
@ -0,0 +1,26 @@ |
|||||||
|
#ifndef FLUTTER_PLUGIN_EMBEDDER_TEXTURE_PLUGIN_H_ |
||||||
|
#define FLUTTER_PLUGIN_EMBEDDER_TEXTURE_PLUGIN_H_ |
||||||
|
|
||||||
|
#include <flutter_linux/flutter_linux.h> |
||||||
|
|
||||||
|
G_BEGIN_DECLS |
||||||
|
|
||||||
|
#ifdef FLUTTER_PLUGIN_IMPL |
||||||
|
#define FLUTTER_PLUGIN_EXPORT __attribute__((visibility("default"))) |
||||||
|
#else |
||||||
|
#define FLUTTER_PLUGIN_EXPORT |
||||||
|
#endif |
||||||
|
|
||||||
|
typedef struct _LinuxOpenglPlugin LinuxOpenglPlugin; |
||||||
|
typedef struct { |
||||||
|
GObjectClass parent_class; |
||||||
|
} LinuxOpenglPluginClass; |
||||||
|
|
||||||
|
FLUTTER_PLUGIN_EXPORT GType embedder_texture_plugin_get_type(); |
||||||
|
|
||||||
|
FLUTTER_PLUGIN_EXPORT void embedder_texture_plugin_register_with_registrar( |
||||||
|
FlPluginRegistrar* registrar); |
||||||
|
|
||||||
|
G_END_DECLS |
||||||
|
|
||||||
|
#endif // FLUTTER_PLUGIN_EMBEDDER_TEXTURE_PLUGIN_H_
|
@ -0,0 +1,28 @@ |
|||||||
|
#ifndef FLUTTER_MY_TEXTURE_H |
||||||
|
#define FLUTTER_MY_TEXTURE_H |
||||||
|
|
||||||
|
#include <gtk/gtk.h> |
||||||
|
#include <glib-object.h> |
||||||
|
#include "embedder_texture/embedder_texture_plugin.h" |
||||||
|
#include <flutter_linux/flutter_linux.h> |
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE(FlMyTextureGL, |
||||||
|
fl_my_texture_gl, |
||||||
|
FL, |
||||||
|
MY_TEXTURE_GL, |
||||||
|
FlTextureGL) |
||||||
|
|
||||||
|
struct _FlMyTextureGL |
||||||
|
{ |
||||||
|
FlTextureGL parent_instance; |
||||||
|
uint32_t target; |
||||||
|
uint32_t name; |
||||||
|
uint32_t width; |
||||||
|
uint32_t height; |
||||||
|
}; |
||||||
|
|
||||||
|
FlMyTextureGL *fl_my_texture_gl_new(uint32_t target, |
||||||
|
uint32_t name, |
||||||
|
uint32_t width, |
||||||
|
uint32_t height); |
||||||
|
#endif // FLUTTER_MY_TEXTURE_H
|
@ -0,0 +1,25 @@ |
|||||||
|
name: embedder_texture |
||||||
|
description: Example create texture. |
||||||
|
version: 0.0.1 |
||||||
|
|
||||||
|
environment: |
||||||
|
sdk: '>=3.0.6 <4.0.0' |
||||||
|
flutter: ">=3.3.0" |
||||||
|
|
||||||
|
dependencies: |
||||||
|
flutter: |
||||||
|
sdk: flutter |
||||||
|
plugin_platform_interface: ^2.0.2 |
||||||
|
|
||||||
|
dev_dependencies: |
||||||
|
flutter_test: |
||||||
|
sdk: flutter |
||||||
|
flutter_lints: ^2.0.0 |
||||||
|
|
||||||
|
flutter: |
||||||
|
plugin: |
||||||
|
platforms: |
||||||
|
linux: |
||||||
|
pluginClass: EmbedderTexturePlugin |
||||||
|
aurora: |
||||||
|
pluginClass: EmbedderTexturePlugin |
Loading…
Reference in new issue