2017-08-20 16:17:24 +02:00
/**************************************************************************/
/* camera_feed.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2019-06-17 12:42:05 +02:00
/* https://godotengine.org */
2017-08-20 16:17:24 +02:00
/**************************************************************************/
2019-06-17 12:42:05 +02:00
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
2017-08-20 16:17:24 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
# include "camera_feed.h"
2019-06-11 20:43:37 +02:00
2020-03-27 19:21:27 +01:00
# include "servers/rendering_server.h"
2017-08-20 16:17:24 +02:00
void CameraFeed : : _bind_methods ( ) {
ClassDB : : bind_method ( D_METHOD ( " get_id " ) , & CameraFeed : : get_id ) ;
ClassDB : : bind_method ( D_METHOD ( " is_active " ) , & CameraFeed : : is_active ) ;
ClassDB : : bind_method ( D_METHOD ( " set_active " , " active " ) , & CameraFeed : : set_active ) ;
2021-08-12 19:27:11 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_name " ) , & CameraFeed : : get_name ) ;
2024-09-27 12:10:59 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_name " , " name " ) , & CameraFeed : : set_name ) ;
2021-08-12 19:27:11 +02:00
2017-08-20 16:17:24 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_position " ) , & CameraFeed : : get_position ) ;
2024-09-27 12:10:59 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_position " , " position " ) , & CameraFeed : : set_position ) ;
2017-08-20 16:17:24 +02:00
// Note, for transform some feeds may override what the user sets (such as ARKit)
ClassDB : : bind_method ( D_METHOD ( " get_transform " ) , & CameraFeed : : get_transform ) ;
ClassDB : : bind_method ( D_METHOD ( " set_transform " , " transform " ) , & CameraFeed : : set_transform ) ;
2024-09-27 12:10:59 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_rgb_image " , " rgb_image " ) , & CameraFeed : : set_rgb_image ) ;
ClassDB : : bind_method ( D_METHOD ( " set_ycbcr_image " , " ycbcr_image " ) , & CameraFeed : : set_ycbcr_image ) ;
2021-08-12 19:27:11 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_datatype " ) , & CameraFeed : : get_datatype ) ;
2017-08-20 16:17:24 +02:00
2023-06-05 18:56:34 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_formats " ) , & CameraFeed : : get_formats ) ;
ClassDB : : bind_method ( D_METHOD ( " set_format " , " index " , " parameters " ) , & CameraFeed : : set_format ) ;
ADD_SIGNAL ( MethodInfo ( " frame_changed " ) ) ;
ADD_SIGNAL ( MethodInfo ( " format_changed " ) ) ;
2017-08-20 16:17:24 +02:00
ADD_GROUP ( " Feed " , " feed_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " feed_is_active " ) , " set_active " , " is_active " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : TRANSFORM2D , " feed_transform " ) , " set_transform " , " get_transform " ) ;
2023-06-05 18:56:34 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : ARRAY , " formats " ) , " " , " get_formats " ) ;
2017-08-20 16:17:24 +02:00
BIND_ENUM_CONSTANT ( FEED_NOIMAGE ) ;
BIND_ENUM_CONSTANT ( FEED_RGB ) ;
2019-06-19 14:21:13 +02:00
BIND_ENUM_CONSTANT ( FEED_YCBCR ) ;
BIND_ENUM_CONSTANT ( FEED_YCBCR_SEP ) ;
2017-08-20 16:17:24 +02:00
BIND_ENUM_CONSTANT ( FEED_UNSPECIFIED ) ;
BIND_ENUM_CONSTANT ( FEED_FRONT ) ;
BIND_ENUM_CONSTANT ( FEED_BACK ) ;
}
int CameraFeed : : get_id ( ) const {
return id ;
}
bool CameraFeed : : is_active ( ) const {
return active ;
}
void CameraFeed : : set_active ( bool p_is_active ) {
if ( p_is_active = = active ) {
// all good
} else if ( p_is_active ) {
// attempt to activate this feed
if ( activate_feed ( ) ) {
active = true ;
}
} else {
// just deactivate it
deactivate_feed ( ) ;
active = false ;
}
}
String CameraFeed : : get_name ( ) const {
return name ;
}
void CameraFeed : : set_name ( String p_name ) {
name = p_name ;
}
int CameraFeed : : get_base_width ( ) const {
return base_width ;
}
int CameraFeed : : get_base_height ( ) const {
return base_height ;
}
CameraFeed : : FeedDataType CameraFeed : : get_datatype ( ) const {
return datatype ;
}
CameraFeed : : FeedPosition CameraFeed : : get_position ( ) const {
return position ;
}
void CameraFeed : : set_position ( CameraFeed : : FeedPosition p_position ) {
position = p_position ;
}
Transform2D CameraFeed : : get_transform ( ) const {
return transform ;
}
void CameraFeed : : set_transform ( const Transform2D & p_transform ) {
transform = p_transform ;
}
RID CameraFeed : : get_texture ( CameraServer : : FeedImage p_which ) {
return texture [ p_which ] ;
}
CameraFeed : : CameraFeed ( ) {
// initialize our feed
id = CameraServer : : get_singleton ( ) - > get_free_id ( ) ;
2021-08-13 11:32:01 +02:00
base_width = 0 ;
base_height = 0 ;
2017-08-20 16:17:24 +02:00
name = " ??? " ;
active = false ;
datatype = CameraFeed : : FEED_RGB ;
position = CameraFeed : : FEED_UNSPECIFIED ;
transform = Transform2D ( 1.0 , 0.0 , 0.0 , - 1.0 , 0.0 , 1.0 ) ;
2021-08-13 11:32:01 +02:00
texture [ CameraServer : : FEED_Y_IMAGE ] = RenderingServer : : get_singleton ( ) - > texture_2d_placeholder_create ( ) ;
texture [ CameraServer : : FEED_CBCR_IMAGE ] = RenderingServer : : get_singleton ( ) - > texture_2d_placeholder_create ( ) ;
2017-08-20 16:17:24 +02:00
}
CameraFeed : : CameraFeed ( String p_name , FeedPosition p_position ) {
// initialize our feed
id = CameraServer : : get_singleton ( ) - > get_free_id ( ) ;
base_width = 0 ;
base_height = 0 ;
name = p_name ;
active = false ;
datatype = CameraFeed : : FEED_NOIMAGE ;
position = p_position ;
transform = Transform2D ( 1.0 , 0.0 , 0.0 , - 1.0 , 0.0 , 1.0 ) ;
2021-08-13 11:32:01 +02:00
texture [ CameraServer : : FEED_Y_IMAGE ] = RenderingServer : : get_singleton ( ) - > texture_2d_placeholder_create ( ) ;
texture [ CameraServer : : FEED_CBCR_IMAGE ] = RenderingServer : : get_singleton ( ) - > texture_2d_placeholder_create ( ) ;
2017-08-20 16:17:24 +02:00
}
CameraFeed : : ~ CameraFeed ( ) {
// Free our textures
2022-12-12 18:42:37 +01:00
ERR_FAIL_NULL ( RenderingServer : : get_singleton ( ) ) ;
2021-08-13 11:32:01 +02:00
RenderingServer : : get_singleton ( ) - > free ( texture [ CameraServer : : FEED_Y_IMAGE ] ) ;
RenderingServer : : get_singleton ( ) - > free ( texture [ CameraServer : : FEED_CBCR_IMAGE ] ) ;
2017-08-20 16:17:24 +02:00
}
2024-09-27 12:10:59 +02:00
void CameraFeed : : set_rgb_image ( const Ref < Image > & p_rgb_img ) {
2021-03-14 10:47:21 +01:00
ERR_FAIL_COND ( p_rgb_img . is_null ( ) ) ;
2017-08-20 16:17:24 +02:00
if ( active ) {
int new_width = p_rgb_img - > get_width ( ) ;
int new_height = p_rgb_img - > get_height ( ) ;
if ( ( base_width ! = new_width ) | | ( base_height ! = new_height ) ) {
// We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
base_width = new_width ;
base_height = new_height ;
2021-08-13 11:32:01 +02:00
RID new_texture = RenderingServer : : get_singleton ( ) - > texture_2d_create ( p_rgb_img ) ;
RenderingServer : : get_singleton ( ) - > texture_replace ( texture [ CameraServer : : FEED_RGBA_IMAGE ] , new_texture ) ;
2023-06-05 18:56:34 +02:00
emit_signal ( SNAME ( " format_changed " ) ) ;
2021-08-12 19:27:11 +02:00
} else {
RenderingServer : : get_singleton ( ) - > texture_2d_update ( texture [ CameraServer : : FEED_RGBA_IMAGE ] , p_rgb_img ) ;
2017-08-20 16:17:24 +02:00
}
datatype = CameraFeed : : FEED_RGB ;
}
}
2024-09-27 12:10:59 +02:00
void CameraFeed : : set_ycbcr_image ( const Ref < Image > & p_ycbcr_img ) {
2021-03-14 10:47:21 +01:00
ERR_FAIL_COND ( p_ycbcr_img . is_null ( ) ) ;
2017-08-20 16:17:24 +02:00
if ( active ) {
int new_width = p_ycbcr_img - > get_width ( ) ;
int new_height = p_ycbcr_img - > get_height ( ) ;
if ( ( base_width ! = new_width ) | | ( base_height ! = new_height ) ) {
// We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
base_width = new_width ;
base_height = new_height ;
2021-08-13 11:32:01 +02:00
RID new_texture = RenderingServer : : get_singleton ( ) - > texture_2d_create ( p_ycbcr_img ) ;
RenderingServer : : get_singleton ( ) - > texture_replace ( texture [ CameraServer : : FEED_RGBA_IMAGE ] , new_texture ) ;
2023-06-05 18:56:34 +02:00
emit_signal ( SNAME ( " format_changed " ) ) ;
2021-08-12 19:27:11 +02:00
} else {
RenderingServer : : get_singleton ( ) - > texture_2d_update ( texture [ CameraServer : : FEED_RGBA_IMAGE ] , p_ycbcr_img ) ;
2017-08-20 16:17:24 +02:00
}
2019-06-19 14:21:13 +02:00
datatype = CameraFeed : : FEED_YCBCR ;
2017-08-20 16:17:24 +02:00
}
}
2024-09-27 12:10:59 +02:00
void CameraFeed : : set_ycbcr_images ( const Ref < Image > & p_y_img , const Ref < Image > & p_cbcr_img ) {
2021-03-14 10:47:21 +01:00
ERR_FAIL_COND ( p_y_img . is_null ( ) ) ;
ERR_FAIL_COND ( p_cbcr_img . is_null ( ) ) ;
2017-08-20 16:17:24 +02:00
if ( active ) {
///@TODO investigate whether we can use thirdparty/misc/yuv2rgb.h here to convert our YUV data to RGB, our shader approach is potentially faster though..
// Wondering about including that into multiple projects, may cause issues.
// That said, if we convert to RGB, we could enable using texture resources again...
int new_y_width = p_y_img - > get_width ( ) ;
int new_y_height = p_y_img - > get_height ( ) ;
if ( ( base_width ! = new_y_width ) | | ( base_height ! = new_y_height ) ) {
// We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
base_width = new_y_width ;
base_height = new_y_height ;
2021-08-13 11:32:01 +02:00
{
2021-08-12 19:27:11 +02:00
RID new_texture = RenderingServer : : get_singleton ( ) - > texture_2d_create ( p_y_img ) ;
RenderingServer : : get_singleton ( ) - > texture_replace ( texture [ CameraServer : : FEED_Y_IMAGE ] , new_texture ) ;
}
2021-08-13 11:32:01 +02:00
{
2021-08-12 19:27:11 +02:00
RID new_texture = RenderingServer : : get_singleton ( ) - > texture_2d_create ( p_cbcr_img ) ;
RenderingServer : : get_singleton ( ) - > texture_replace ( texture [ CameraServer : : FEED_CBCR_IMAGE ] , new_texture ) ;
}
2023-06-05 18:56:34 +02:00
emit_signal ( SNAME ( " format_changed " ) ) ;
2021-08-12 19:27:11 +02:00
} else {
RenderingServer : : get_singleton ( ) - > texture_2d_update ( texture [ CameraServer : : FEED_Y_IMAGE ] , p_y_img ) ;
RenderingServer : : get_singleton ( ) - > texture_2d_update ( texture [ CameraServer : : FEED_CBCR_IMAGE ] , p_cbcr_img ) ;
2017-08-20 16:17:24 +02:00
}
2019-06-19 14:21:13 +02:00
datatype = CameraFeed : : FEED_YCBCR_SEP ;
2017-08-20 16:17:24 +02:00
}
}
bool CameraFeed : : activate_feed ( ) {
// nothing to do here
return true ;
}
void CameraFeed : : deactivate_feed ( ) {
// nothing to do here
}
2023-06-05 18:56:34 +02:00
bool CameraFeed : : set_format ( int p_index , const Dictionary & p_parameters ) {
return false ;
}
Array CameraFeed : : get_formats ( ) const {
return Array ( ) ;
}
CameraFeed : : FeedFormat CameraFeed : : get_format ( ) const {
FeedFormat feed_format = { } ;
return feed_format ;
}