2017-08-20 14:17:24 +00:00
/*************************************************************************/
/* camera_feed.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2019-06-17 10:42:05 +00:00
/* https://godotengine.org */
2017-08-20 14:17:24 +00:00
/*************************************************************************/
2022-01-13 08:45:09 +00:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2017-08-20 14:17:24 +00: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"
# include "servers/visual_server.h"
void CameraFeed : : _bind_methods ( ) {
// The setters prefixed with _ are only exposed so we can have feeds through GDNative!
// They should not be called by the end user.
ClassDB : : bind_method ( D_METHOD ( " get_id " ) , & CameraFeed : : get_id ) ;
ClassDB : : bind_method ( D_METHOD ( " get_name " ) , & CameraFeed : : get_name ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_name " , " name " ) , & CameraFeed : : set_name ) ;
ClassDB : : bind_method ( D_METHOD ( " is_active " ) , & CameraFeed : : is_active ) ;
ClassDB : : bind_method ( D_METHOD ( " set_active " , " active " ) , & CameraFeed : : set_active ) ;
ClassDB : : bind_method ( D_METHOD ( " get_position " ) , & CameraFeed : : get_position ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_position " , " position " ) , & CameraFeed : : set_position ) ;
// 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 ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_RGB_img " , " rgb_img " ) , & CameraFeed : : set_RGB_img ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_YCbCr_img " , " ycbcr_img " ) , & CameraFeed : : set_YCbCr_img ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_YCbCr_imgs " , " y_img " , " cbcr_img " ) , & CameraFeed : : set_YCbCr_imgs ) ;
ClassDB : : bind_method ( D_METHOD ( " _allocate_texture " , " width " , " height " , " format " , " texture_type " , " data_type " ) , & CameraFeed : : allocate_texture ) ;
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 " ) ;
BIND_ENUM_CONSTANT ( FEED_NOIMAGE ) ;
BIND_ENUM_CONSTANT ( FEED_RGB ) ;
2019-06-19 12:21:13 +00:00
BIND_ENUM_CONSTANT ( FEED_YCBCR ) ;
BIND_ENUM_CONSTANT ( FEED_YCBCR_SEP ) ;
2017-08-20 14:17:24 +00: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 ( ) ) {
print_line ( " Activate " + name ) ;
active = true ;
}
} else {
// just deactivate it
deactivate_feed ( ) ;
print_line ( " Deactivate " + name ) ;
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 ( ) ;
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 ) ;
// create a texture object
VisualServer * vs = VisualServer : : get_singleton ( ) ;
2021-11-09 12:00:07 +00:00
texture [ CameraServer : : FEED_Y_IMAGE ] = RID_PRIME ( vs - > texture_create ( ) ) ; // also used for RGBA
texture [ CameraServer : : FEED_CBCR_IMAGE ] = RID_PRIME ( vs - > texture_create ( ) ) ;
2017-08-20 14:17:24 +00: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 ) ;
// create a texture object
VisualServer * vs = VisualServer : : get_singleton ( ) ;
2021-11-09 12:00:07 +00:00
texture [ CameraServer : : FEED_Y_IMAGE ] = RID_PRIME ( vs - > texture_create ( ) ) ; // also used for RGBA
texture [ CameraServer : : FEED_CBCR_IMAGE ] = RID_PRIME ( vs - > texture_create ( ) ) ;
2017-08-20 14:17:24 +00:00
}
CameraFeed : : ~ CameraFeed ( ) {
// Free our textures
VisualServer * vs = VisualServer : : get_singleton ( ) ;
vs - > free ( texture [ CameraServer : : FEED_Y_IMAGE ] ) ;
2019-06-19 12:21:13 +00:00
vs - > free ( texture [ CameraServer : : FEED_CBCR_IMAGE ] ) ;
2017-08-20 14:17:24 +00:00
}
2021-03-14 09:47:21 +00:00
void CameraFeed : : set_RGB_img ( const Ref < Image > & p_rgb_img ) {
ERR_FAIL_COND ( p_rgb_img . is_null ( ) ) ;
2017-08-20 14:17:24 +00:00
if ( active ) {
VisualServer * vs = VisualServer : : get_singleton ( ) ;
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 ;
vs - > texture_allocate ( texture [ CameraServer : : FEED_RGBA_IMAGE ] , new_width , new_height , 0 , Image : : FORMAT_RGB8 , VS : : TEXTURE_TYPE_2D , VS : : TEXTURE_FLAGS_DEFAULT ) ;
}
vs - > texture_set_data ( texture [ CameraServer : : FEED_RGBA_IMAGE ] , p_rgb_img ) ;
datatype = CameraFeed : : FEED_RGB ;
}
}
2021-03-14 09:47:21 +00:00
void CameraFeed : : set_YCbCr_img ( const Ref < Image > & p_ycbcr_img ) {
ERR_FAIL_COND ( p_ycbcr_img . is_null ( ) ) ;
2017-08-20 14:17:24 +00:00
if ( active ) {
VisualServer * vs = VisualServer : : get_singleton ( ) ;
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 ;
vs - > texture_allocate ( texture [ CameraServer : : FEED_RGBA_IMAGE ] , new_width , new_height , 0 , Image : : FORMAT_RGB8 , VS : : TEXTURE_TYPE_2D , VS : : TEXTURE_FLAGS_DEFAULT ) ;
}
vs - > texture_set_data ( texture [ CameraServer : : FEED_RGBA_IMAGE ] , p_ycbcr_img ) ;
2019-06-19 12:21:13 +00:00
datatype = CameraFeed : : FEED_YCBCR ;
2017-08-20 14:17:24 +00:00
}
}
2021-03-14 09:47:21 +00:00
void CameraFeed : : set_YCbCr_imgs ( const Ref < Image > & p_y_img , const Ref < Image > & p_cbcr_img ) {
ERR_FAIL_COND ( p_y_img . is_null ( ) ) ;
ERR_FAIL_COND ( p_cbcr_img . is_null ( ) ) ;
2017-08-20 14:17:24 +00:00
if ( active ) {
VisualServer * vs = VisualServer : : get_singleton ( ) ;
///@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 ( ) ;
int new_cbcr_width = p_cbcr_img - > get_width ( ) ;
int new_cbcr_height = p_cbcr_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 ;
vs - > texture_allocate ( texture [ CameraServer : : FEED_Y_IMAGE ] , new_y_width , new_y_height , 0 , Image : : FORMAT_R8 , VS : : TEXTURE_TYPE_2D , VS : : TEXTURE_FLAG_USED_FOR_STREAMING ) ;
///@TODO GLES2 doesn't support FORMAT_RG8, need to do some form of conversion
2019-06-19 12:21:13 +00:00
vs - > texture_allocate ( texture [ CameraServer : : FEED_CBCR_IMAGE ] , new_cbcr_width , new_cbcr_height , 0 , Image : : FORMAT_RG8 , VS : : TEXTURE_TYPE_2D , VS : : TEXTURE_FLAG_USED_FOR_STREAMING ) ;
2017-08-20 14:17:24 +00:00
}
vs - > texture_set_data ( texture [ CameraServer : : FEED_Y_IMAGE ] , p_y_img ) ;
2019-06-19 12:21:13 +00:00
vs - > texture_set_data ( texture [ CameraServer : : FEED_CBCR_IMAGE ] , p_cbcr_img ) ;
datatype = CameraFeed : : FEED_YCBCR_SEP ;
2017-08-20 14:17:24 +00:00
}
}
void CameraFeed : : allocate_texture ( int p_width , int p_height , Image : : Format p_format , VisualServer : : TextureType p_texture_type , FeedDataType p_data_type ) {
VisualServer * vs = VisualServer : : get_singleton ( ) ;
if ( ( base_width ! = p_width ) | | ( base_height ! = p_height ) ) {
// We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
base_width = p_width ;
base_height = p_height ;
vs - > texture_allocate ( texture [ 0 ] , p_width , p_height , 0 , p_format , p_texture_type , VS : : TEXTURE_FLAGS_DEFAULT ) ;
}
datatype = p_data_type ;
}
bool CameraFeed : : activate_feed ( ) {
// nothing to do here
return true ;
}
void CameraFeed : : deactivate_feed ( ) {
// nothing to do here
}