1 /*
2 * Copyright (C) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "PostProcJS.h"
17
18 #include <meta/api/make_callback.h>
19 #include <meta/api/util.h>
20 #include <meta/interface/intf_task_queue.h>
21 #include <meta/interface/intf_task_queue_registry.h>
22 #include <meta/interface/property/property_events.h>
23 #include <scene/interface/intf_node.h>
24 #include <scene/interface/intf_scene.h>
25
26 #include <render/intf_render_context.h>
27
28 #include "BloomJS.h"
29 #include "CameraJS.h"
30 #include "ToneMapJS.h"
31 using IntfPtr = BASE_NS::shared_ptr<CORE_NS::IInterface>;
32 using IntfWeakPtr = BASE_NS::weak_ptr<CORE_NS::IInterface>;
33 using namespace SCENE_NS;
34
Init(napi_env env,napi_value exports)35 void PostProcJS::Init(napi_env env, napi_value exports)
36 {
37 using namespace NapiApi;
38
39 BASE_NS::vector<napi_property_descriptor> node_props;
40 // clang-format off
41
42 node_props.push_back(GetSetProperty<Object, PostProcJS, &PostProcJS::GetBloom, &PostProcJS::SetBloom>("bloom"));
43 node_props.emplace_back(
44 GetSetProperty<Object, PostProcJS, &PostProcJS::GetToneMapping, &PostProcJS::SetToneMapping>("toneMapping"));
45 node_props.push_back(MakeTROMethod<NapiApi::FunctionContext<>, PostProcJS, &PostProcJS::Dispose>("destroy"));
46
47 // clang-format on
48
49 napi_value func;
50 auto status = napi_define_class(env, "PostProcessSettings", NAPI_AUTO_LENGTH, BaseObject::ctor<PostProcJS>(),
51 nullptr, node_props.size(), node_props.data(), &func);
52
53 NapiApi::MyInstanceState* mis;
54 NapiApi::MyInstanceState::GetInstance(env, (void**)&mis);
55 if (mis) {
56 mis->StoreCtor("PostProcessSettings", func);
57 }
58 }
59
Dispose(NapiApi::FunctionContext<> & ctx)60 napi_value PostProcJS::Dispose(NapiApi::FunctionContext<>& ctx)
61 {
62 LOG_V("PostProcJS::Dispose");
63 // see if we have "scenejs" as ext (prefer one given as argument)
64 napi_status stat;
65 CameraJS* ptr { nullptr };
66 NapiApi::FunctionContext<NapiApi::Object> args(ctx);
67 if (args) {
68 if (NapiApi::Object obj = args.Arg<0>()) {
69 if (napi_value ext = obj.Get("CameraJS")) {
70 stat = napi_get_value_external(ctx.GetEnv(), ext, (void**)&ptr);
71 }
72 }
73 }
74 if (!ptr) {
75 ptr = camera_.GetObject().GetJsWrapper<CameraJS>();
76 }
77
78 DisposeNative(ptr);
79 return {};
80 }
DisposeNative(void * cam)81 void PostProcJS::DisposeNative(void* cam)
82 {
83 if (cam == nullptr) {
84 if (!disposed_) {
85 LOG_F("PostProcJS::DisposeNative but argument NULL");
86 }
87 return;
88 }
89 if (!disposed_) {
90 disposed_ = true;
91 LOG_V("PostProcJS::DisposeNative");
92 // make sure we release toneMap settings
93
94 // Detach this wrapper and its members tonemap and bloom from the native objects.
95 if (auto tmjs = toneMap_.GetObject()) {
96 NapiApi::Function func = tmjs.Get<NapiApi::Function>("destroy");
97 if (func) {
98 func.Invoke(tmjs);
99 }
100 }
101 toneMap_.Reset();
102
103 if (auto bmjs = bloom_.GetObject()) {
104 NapiApi::Function func = bmjs.Get<NapiApi::Function>("destroy");
105 if (func) {
106 func.Invoke(bmjs);
107 }
108 }
109 bloom_.Reset();
110 if (auto post = interface_pointer_cast<IPostProcess>(GetNativeObject())) {
111 UnsetNativeObject();
112 if (const auto cam = camera_.GetObject().GetJsWrapper<CameraJS>()) {
113 ExecSyncTask([cam, post = BASE_NS::move(post)]() {
114 cam->ReleaseObject(interface_pointer_cast<META_NS::IObject>(post));
115 return META_NS::IAny::Ptr {};
116 });
117 }
118 }
119 }
120 }
GetInstanceImpl(uint32_t id)121 void* PostProcJS::GetInstanceImpl(uint32_t id)
122 {
123 if (id == PostProcJS::ID)
124 return this;
125 return nullptr;
126 }
Finalize(napi_env env)127 void PostProcJS::Finalize(napi_env env)
128 {
129 DisposeNative(camera_.GetObject().GetJsWrapper<CameraJS>());
130 BaseObject::Finalize(env);
131 }
132
PostProcJS(napi_env e,napi_callback_info i)133 PostProcJS::PostProcJS(napi_env e, napi_callback_info i) : BaseObject(e, i)
134 {
135 LOG_V("PostProcJS ++");
136 NapiApi::FunctionContext<NapiApi::Object, NapiApi::Object> fromJs(e, i);
137 if (!fromJs) {
138 // no arguments. so internal create.
139 // expecting caller to finish
140 return;
141 }
142
143 // camera that we bind to..
144 NapiApi::Object cameraJS = fromJs.Arg<0>();
145 camera_ = { cameraJS };
146
147 auto postproc = GetNativeObject<SCENE_NS::IPostProcess>();
148 if (!postproc) {
149 LOG_E("Error creating PostProcessSettings: No native object given");
150 assert(false);
151 return;
152 }
153
154 // process constructor args..
155 NapiApi::Object meJs(fromJs.This());
156 // now, based on parameters, create correct objects.
157 if (NapiApi::Object args = fromJs.Arg<1>()) {
158 if (auto prm = args.Get("toneMapping")) {
159 // enable tonemap.
160 napi_value innerArgs[] = { prm };
161 const auto tone = postproc->Tonemap()->GetValue();
162 // The tonemap is a part of the PostProcess object, so we own it. TonemapJS get only a weak ref.
163 const auto tonemapJS = CreateFromNativeInstance(e, tone, PtrType::WEAK, innerArgs);
164 meJs.Set("toneMapping", tonemapJS);
165 }
166 if (auto prm = args.Get("bloom")) {
167 meJs.Set("bloom", prm);
168 }
169 }
170 }
171
~PostProcJS()172 PostProcJS::~PostProcJS()
173 {
174 LOG_V("PostProcJS --");
175 DisposeNative(nullptr);
176 if (!GetNativeObject()) {
177 return;
178 }
179 }
180
SetToneMapping(NapiApi::FunctionContext<NapiApi::Object> & ctx)181 void PostProcJS::SetToneMapping(NapiApi::FunctionContext<NapiApi::Object>& ctx)
182 {
183 auto postproc = interface_cast<SCENE_NS::IPostProcess>(GetNativeObject());
184 if (!postproc) {
185 // not possible.
186 return;
187 }
188 NapiApi::Object tonemapJS = ctx.Arg<0>();
189
190 if (auto currentlySet = toneMap_.GetObject()) {
191 if (tonemapJS.StrictEqual(currentlySet)) {
192 return;
193 }
194 if (const auto wrapper = currentlySet.GetJsWrapper<ToneMapJS>()) {
195 // Free the old tonemap so it isn't bound to the changes happening in the new one.
196 wrapper->UnbindFromNative();
197 }
198 }
199 auto target = postproc->Tonemap()->GetValue();
200 if (!target) {
201 return;
202 }
203
204 // If the new JS tonemap is bound to a native tonemap, we have this problem:
205 // Should set the tonemap of both cameras but won't.
206 auto source = tonemapJS.GetNative<SCENE_NS::ITonemap>();
207 if (tonemapJS.IsNull()) {
208 META_NS::SetValue(target->Enabled(), false);
209 } else {
210 const auto newType = tonemapJS.Get<uint32_t>("type").valueOrDefault(ToneMapJS::DEFAULT_TYPE);
211 const auto newExposure = tonemapJS.Get<float>("exposure").valueOrDefault(ToneMapJS::DEFAULT_EXPOSURE);
212 META_NS::SetValue(target->Type(), ToneMapJS::ToNativeType(newType));
213 META_NS::SetValue(target->Exposure(), newExposure);
214 META_NS::SetValue(target->Enabled(), true);
215 }
216 toneMap_ = NapiApi::StrongRef(tonemapJS);
217 }
218
GetToneMapping(NapiApi::FunctionContext<> & ctx)219 napi_value PostProcJS::GetToneMapping(NapiApi::FunctionContext<>& ctx)
220 {
221 if (auto postproc = interface_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
222 SCENE_NS::ITonemap::Ptr tone = META_NS::GetValue(postproc->Tonemap());
223 if ((!tone) || (!META_NS::GetValue(tone->Enabled(), false))) {
224 // no tonemap object or tonemap disabled.
225 return ctx.GetUndefined();
226 }
227 auto tonemapJS = CreateFromNativeInstance(ctx.GetEnv(), tone, PtrType::WEAK, {});
228 toneMap_ = NapiApi::StrongRef(tonemapJS); // take ownership of the object.
229 return tonemapJS.ToNapiValue();
230 }
231 toneMap_.Reset();
232 return ctx.GetUndefined();
233 }
GetBloom(NapiApi::FunctionContext<> & ctx)234 napi_value PostProcJS::GetBloom(NapiApi::FunctionContext<>& ctx)
235 {
236 if (!bloom_.IsEmpty()) {
237 // okay return the existing one.
238 return bloom_.GetValue();
239 }
240
241 BloomConfiguration* data = nullptr;
242 if (auto postproc = interface_pointer_cast<SCENE_NS::IPostProcess>(GetNativeObject())) {
243 auto bloom = postproc->Bloom()->GetValue();
244 if (bloom->Enabled()->GetValue()) {
245 data = new BloomConfiguration();
246 data->SetFrom(bloom);
247 data->SetPostProc(postproc);
248 }
249 }
250
251 if (data == nullptr) {
252 return ctx.GetUndefined();
253 }
254
255 NapiApi::Env env(ctx.GetEnv());
256 NapiApi::Object obj(env);
257 bloom_ = BASE_NS::move(data->Wrap(obj));
258 return bloom_.GetValue();
259 }
260
SetBloom(NapiApi::FunctionContext<NapiApi::Object> & ctx)261 void PostProcJS::SetBloom(NapiApi::FunctionContext<NapiApi::Object>& ctx)
262 {
263 auto postproc = GetNativeObject<SCENE_NS::IPostProcess>();
264 if (!postproc) {
265 return;
266 }
267
268 bool enabled = false;
269 if (const auto bloomJs = NapiApi::Object { ctx.Arg<0>() }) {
270 enabled = true;
271 // is it wrapped?
272 if (auto wrapper = BloomConfiguration::Unwrap(bloomJs)) {
273 if (const auto newPostProc = wrapper->GetPostProc(); newPostProc != postproc) {
274 // Can't use the wrapper of another postprocess object yet.
275 LOG_F("Tried to attach a bloom wrapper object that was already bound to another one");
276 return;
277 }
278 // The new bloom is wrapped, but has no native postproc (or we are already its native).
279 wrapper->SetPostProc(postproc);
280 bloom_ = NapiApi::StrongRef(bloomJs);
281 } else {
282 wrapper = new BloomConfiguration();
283 wrapper->SetFrom(bloomJs);
284 wrapper->SetPostProc(postproc);
285 bloom_ = wrapper->Wrap(bloomJs);
286 }
287 } else {
288 // disabling bloom.. get current, and unwrap it.
289 auto oldBloom = bloom_.GetObject();
290 if (auto oldWrapper = BloomConfiguration::Unwrap(oldBloom)) {
291 // detaches native and javascript object.
292 oldWrapper->SetPostProc(nullptr); // detach from object
293 }
294 // release our reference to the current bloom settings (JS)
295 bloom_.Reset();
296 }
297
298 if (auto bloom = postproc->Bloom()->GetValue()) {
299 bloom->Enabled()->SetValue(enabled);
300 // Poke postProc so that the changes to bloom are updated.
301 }
302 }
303