1/**************************************************************************/
2/* camera_feed.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "camera_feed.h"
32
33#include "servers/rendering_server.h"
34
35void CameraFeed::_bind_methods() {
36 // The setters prefixed with _ are only exposed so we can have feeds through GDExtension!
37 // They should not be called by the end user.
38
39 ClassDB::bind_method(D_METHOD("get_id"), &CameraFeed::get_id);
40
41 ClassDB::bind_method(D_METHOD("is_active"), &CameraFeed::is_active);
42 ClassDB::bind_method(D_METHOD("set_active", "active"), &CameraFeed::set_active);
43
44 ClassDB::bind_method(D_METHOD("get_name"), &CameraFeed::get_name);
45 ClassDB::bind_method(D_METHOD("_set_name", "name"), &CameraFeed::set_name);
46
47 ClassDB::bind_method(D_METHOD("get_position"), &CameraFeed::get_position);
48 ClassDB::bind_method(D_METHOD("_set_position", "position"), &CameraFeed::set_position);
49
50 // Note, for transform some feeds may override what the user sets (such as ARKit)
51 ClassDB::bind_method(D_METHOD("get_transform"), &CameraFeed::get_transform);
52 ClassDB::bind_method(D_METHOD("set_transform", "transform"), &CameraFeed::set_transform);
53
54 ClassDB::bind_method(D_METHOD("_set_RGB_img", "rgb_img"), &CameraFeed::set_RGB_img);
55 ClassDB::bind_method(D_METHOD("_set_YCbCr_img", "ycbcr_img"), &CameraFeed::set_YCbCr_img);
56
57 ClassDB::bind_method(D_METHOD("get_datatype"), &CameraFeed::get_datatype);
58
59 ADD_GROUP("Feed", "feed_");
60 ADD_PROPERTY(PropertyInfo(Variant::BOOL, "feed_is_active"), "set_active", "is_active");
61 ADD_PROPERTY(PropertyInfo(Variant::TRANSFORM2D, "feed_transform"), "set_transform", "get_transform");
62
63 BIND_ENUM_CONSTANT(FEED_NOIMAGE);
64 BIND_ENUM_CONSTANT(FEED_RGB);
65 BIND_ENUM_CONSTANT(FEED_YCBCR);
66 BIND_ENUM_CONSTANT(FEED_YCBCR_SEP);
67
68 BIND_ENUM_CONSTANT(FEED_UNSPECIFIED);
69 BIND_ENUM_CONSTANT(FEED_FRONT);
70 BIND_ENUM_CONSTANT(FEED_BACK);
71}
72
73int CameraFeed::get_id() const {
74 return id;
75}
76
77bool CameraFeed::is_active() const {
78 return active;
79}
80
81void CameraFeed::set_active(bool p_is_active) {
82 if (p_is_active == active) {
83 // all good
84 } else if (p_is_active) {
85 // attempt to activate this feed
86 if (activate_feed()) {
87 print_line("Activate " + name);
88 active = true;
89 }
90 } else {
91 // just deactivate it
92 deactivate_feed();
93 print_line("Deactivate " + name);
94 active = false;
95 }
96}
97
98String CameraFeed::get_name() const {
99 return name;
100}
101
102void CameraFeed::set_name(String p_name) {
103 name = p_name;
104}
105
106int CameraFeed::get_base_width() const {
107 return base_width;
108}
109
110int CameraFeed::get_base_height() const {
111 return base_height;
112}
113
114CameraFeed::FeedDataType CameraFeed::get_datatype() const {
115 return datatype;
116}
117
118CameraFeed::FeedPosition CameraFeed::get_position() const {
119 return position;
120}
121
122void CameraFeed::set_position(CameraFeed::FeedPosition p_position) {
123 position = p_position;
124}
125
126Transform2D CameraFeed::get_transform() const {
127 return transform;
128}
129
130void CameraFeed::set_transform(const Transform2D &p_transform) {
131 transform = p_transform;
132}
133
134RID CameraFeed::get_texture(CameraServer::FeedImage p_which) {
135 return texture[p_which];
136}
137
138CameraFeed::CameraFeed() {
139 // initialize our feed
140 id = CameraServer::get_singleton()->get_free_id();
141 base_width = 0;
142 base_height = 0;
143 name = "???";
144 active = false;
145 datatype = CameraFeed::FEED_RGB;
146 position = CameraFeed::FEED_UNSPECIFIED;
147 transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
148 texture[CameraServer::FEED_Y_IMAGE] = RenderingServer::get_singleton()->texture_2d_placeholder_create();
149 texture[CameraServer::FEED_CBCR_IMAGE] = RenderingServer::get_singleton()->texture_2d_placeholder_create();
150}
151
152CameraFeed::CameraFeed(String p_name, FeedPosition p_position) {
153 // initialize our feed
154 id = CameraServer::get_singleton()->get_free_id();
155 base_width = 0;
156 base_height = 0;
157 name = p_name;
158 active = false;
159 datatype = CameraFeed::FEED_NOIMAGE;
160 position = p_position;
161 transform = Transform2D(1.0, 0.0, 0.0, -1.0, 0.0, 1.0);
162 texture[CameraServer::FEED_Y_IMAGE] = RenderingServer::get_singleton()->texture_2d_placeholder_create();
163 texture[CameraServer::FEED_CBCR_IMAGE] = RenderingServer::get_singleton()->texture_2d_placeholder_create();
164}
165
166CameraFeed::~CameraFeed() {
167 // Free our textures
168 ERR_FAIL_NULL(RenderingServer::get_singleton());
169 RenderingServer::get_singleton()->free(texture[CameraServer::FEED_Y_IMAGE]);
170 RenderingServer::get_singleton()->free(texture[CameraServer::FEED_CBCR_IMAGE]);
171}
172
173void CameraFeed::set_RGB_img(const Ref<Image> &p_rgb_img) {
174 ERR_FAIL_COND(p_rgb_img.is_null());
175 if (active) {
176 int new_width = p_rgb_img->get_width();
177 int new_height = p_rgb_img->get_height();
178
179 if ((base_width != new_width) || (base_height != new_height)) {
180 // We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
181 base_width = new_width;
182 base_height = new_height;
183
184 RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_rgb_img);
185 RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture);
186 } else {
187 RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_rgb_img);
188 }
189
190 datatype = CameraFeed::FEED_RGB;
191 }
192}
193
194void CameraFeed::set_YCbCr_img(const Ref<Image> &p_ycbcr_img) {
195 ERR_FAIL_COND(p_ycbcr_img.is_null());
196 if (active) {
197 int new_width = p_ycbcr_img->get_width();
198 int new_height = p_ycbcr_img->get_height();
199
200 if ((base_width != new_width) || (base_height != new_height)) {
201 // We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
202 base_width = new_width;
203 base_height = new_height;
204
205 RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_ycbcr_img);
206 RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_RGBA_IMAGE], new_texture);
207 } else {
208 RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_RGBA_IMAGE], p_ycbcr_img);
209 }
210
211 datatype = CameraFeed::FEED_YCBCR;
212 }
213}
214
215void CameraFeed::set_YCbCr_imgs(const Ref<Image> &p_y_img, const Ref<Image> &p_cbcr_img) {
216 ERR_FAIL_COND(p_y_img.is_null());
217 ERR_FAIL_COND(p_cbcr_img.is_null());
218 if (active) {
219 ///@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..
220 // Wondering about including that into multiple projects, may cause issues.
221 // That said, if we convert to RGB, we could enable using texture resources again...
222
223 int new_y_width = p_y_img->get_width();
224 int new_y_height = p_y_img->get_height();
225
226 if ((base_width != new_y_width) || (base_height != new_y_height)) {
227 // We're assuming here that our camera image doesn't change around formats etc, allocate the whole lot...
228 base_width = new_y_width;
229 base_height = new_y_height;
230 {
231 RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_y_img);
232 RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_Y_IMAGE], new_texture);
233 }
234 {
235 RID new_texture = RenderingServer::get_singleton()->texture_2d_create(p_cbcr_img);
236 RenderingServer::get_singleton()->texture_replace(texture[CameraServer::FEED_CBCR_IMAGE], new_texture);
237 }
238 } else {
239 RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_Y_IMAGE], p_y_img);
240 RenderingServer::get_singleton()->texture_2d_update(texture[CameraServer::FEED_CBCR_IMAGE], p_cbcr_img);
241 }
242
243 datatype = CameraFeed::FEED_YCBCR_SEP;
244 }
245}
246
247bool CameraFeed::activate_feed() {
248 // nothing to do here
249 return true;
250}
251
252void CameraFeed::deactivate_feed() {
253 // nothing to do here
254}
255