• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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