1 /*
2 * Copyright (c) 2025 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 "ge_contour_diagonal_flow_light_shader.h"
17
18 #include <algorithm>
19 #include <sstream>
20 #include <string>
21 #include <array>
22 #include <iomanip>
23
24 #include "draw/surface.h"
25 #include "ge_log.h"
26 #include "ge_visual_effect_impl.h"
27 #include "common/rs_vector2.h"
28 #include "ge_kawase_blur_shader_filter.h"
29
30 #ifdef USE_M133_SKIA
31 #include "src/core/SkChecksum.h"
32 #else
33 #include "src/core/SkOpts.h"
34 #endif
35
36 namespace OHOS {
37 namespace Rosen {
38
39 using CacheDataType = struct CacheData {
40 std::shared_ptr<Drawing::Image> precalculationImg = nullptr;
41 uint32_t hash = 0;
42 float blurRadius = 0.0f;
43 };
44 using namespace Drawing;
45 namespace {
46 constexpr size_t NUM0 = 0;
47 constexpr size_t NUM1 = 1;
48 constexpr size_t NUM2 = 2;
49 constexpr size_t MIN_NUM = 6;
50 constexpr static uint8_t POSITION_CHANNEL = 2; // 2 floats per point
51 const int MAX_CURVES_PER_GRID = 16;
52 const int MIN_GRID_SIZE = 32;
53 const int XMIN_I = 0;
54 const int XMAX_I = 1;
55 const int YMIN_I = 2;
56 const int YMAX_I = 3;
57
58 // shader
59 static constexpr char FLOW_LIGHT_PROG[] = R"(
60 uniform shader precalculationImage;
61 uniform vec2 iResolution;
62 uniform float line1Start;
63 uniform float line1Length;
64 uniform vec3 line1Color;
65 uniform float line2Start;
66 uniform float line2Length;
67 uniform vec3 line2Color;
68 uniform float lineThickness;
69 // ===== Constant =====
70 const float thickness = 0.04;
71 const float glowBellSigma = 0.28;
72 const float distMin = 1e-4;
73 const float intensity = 1.8;
74 const float glowRadius = 0.005;
75 const float intensityFactor = 0.002;
76
77 // Gaussian bell-shaped profile centered at t = 0.5
78 float BellShape(float t, float sigma)
79 {
80 float x = (t - 0.5) / sigma;
81 return exp(-x * x);
82 }
83
84 // Compute glowing intensity based on SDF and local t on segment
85 float GetSegmentGlowIntensity(float sdfData, float weight, float tLocal)
86 {
87 float w = BellShape(tLocal, glowBellSigma); // tLocal: [0,1]
88 float th = thickness * w * lineThickness;
89 return weight * pow(glowRadius / max(sdfData - th, distMin), intensity) * intensityFactor;
90 }
91
92 void ComputeArcWindow(float tGlobal, float tStart, float windowLen, float sigma,
93 inout float tLocal, inout float weight)
94 {
95 float shiftedT = mod(tGlobal - tStart + 1.0, 1.0);
96 tLocal = shiftedT / windowLen;
97 float inWindow = step(0.0, shiftedT) * step(shiftedT, windowLen);
98 weight = inWindow * BellShape(tLocal, sigma);
99 }
100
101 vec3 SamplePrecalculationImage(vec2 coord)
102 {
103 return precalculationImage.eval(coord).rgb;
104 }
105
106 float DecodeFloat(vec2 rg)
107 {
108 return rg.x + rg.y / 255.0; // 255.0 = maximum value representable in 8-bit channel
109 }
110
111 vec4 main(vec2 fragCoord)
112 {
113 vec2 p = (2.0 * fragCoord - iResolution.xy) / iResolution.y; // 2.0: map uv to [-1, 1]
114 vec3 precalculationData = SamplePrecalculationImage(fragCoord);
115 float sdf = DecodeFloat(precalculationData.rg);
116 float tGlobal = precalculationData.b;
117 if (precalculationImage.eval(fragCoord).a < 0.5) { // 0.5: discard transparent pixels
118 return vec4(0.0, 0.0, 0.0, 0.0);
119 }
120 // === Sliding window for glow arcs ===
121 float weight1 = 0.0;
122 float tLocal1 = 0.0;
123 float weight2 = 0.0;
124 float tLocal2 = 0.0;
125 ComputeArcWindow(tGlobal, line1Start, line1Length, glowBellSigma, tLocal1, weight1);
126 ComputeArcWindow(tGlobal, line2Start, line2Length, glowBellSigma, tLocal2, weight2);
127 // === Glow color composition ===
128 float line1Intensity = GetSegmentGlowIntensity(sdf, weight1, tLocal1);
129 float line2Intensity = GetSegmentGlowIntensity(sdf, weight2, tLocal2);
130 vec3 glowCol1 = line1Color * line1Intensity;
131 vec3 glowCol2 = line2Color * line2Intensity;
132 vec3 glow = glowCol1 + glowCol2;
133 glow *= exp(-sdf * 50.0); // 50.0: the greater, the thinner core
134 float alpha = max(max(glow.r, glow.g), glow.b);
135 return vec4(glow, alpha);
136 }
137 )";
138
139 static constexpr char PRECALCULATION_PROG[] = R"(
140 uniform vec2 iResolution;
141 uniform float count;
142 const int capacity = 48; // 48 control points for 16 Bezier curves
143 uniform vec2 controlPoints[capacity];
144 uniform float segmentIndex[16]; // 16: maximum 16 segments per grid
145
146 // ===== Constant =====
147 const float INF = 1e10; // infinity, i.e., 1.0 / 0.0
148 const float SQRT3 = 1.7320508;
149
150 // ===== Vector Math Utilities =====
151 float Cross2(vec2 a, vec2 b) { return a.x * b.y - a.y * b.x; }
152
153 float SaturateFloat(float a) { return clamp(a, 0.0, 1.0); }
154
155 vec3 SaturateVec3(vec3 a) { return clamp(a, 0.0, 1.0); }
156
157 float AbsMin(float a, float b) { return abs(a) < abs(b) ? a : b; }
158
159 vec2 GetElement(vec2 arr[capacity], int index) {
160 for (int i = 0; i < capacity; ++i) {
161 if (i == index) return arr[i];
162 }
163 return vec2(0.0);
164 }
165
166 vec2 Bezier(vec2 pointA, vec2 pointB, vec2 pointC, float t)
167 {
168 return pointA + t * (-2.0 * pointA + 2.0 * pointB) + t * t * (pointA - 2.0 * pointB + pointC);
169 }
170
171 float SdfLine(vec2 p, vec2 a, vec2 b)
172 {
173 float h = SaturateFloat(dot(p - a, b - a) / dot(b - a, b - a));
174 return length(p - a - h * (b - a));
175 }
176
177 float SdfLinePartition(vec2 p, vec2 a, vec2 b, inout vec2 closestPoint, inout float bestTLocal)
178 {
179 vec2 ba = b - a;
180 vec2 pa = p - a;
181 float h = SaturateFloat(dot(pa, ba) / dot(ba, ba));
182 closestPoint = a + h * ba;
183 bestTLocal = h;
184 vec2 k = pa - h * ba;
185 vec2 n = vec2(ba.y, -ba.x);
186 return (dot(k, n) >= 0.0) ? length(k) : -length(k);
187 }
188
189 float SdfBezier(vec2 pos, vec2 pointA, vec2 pointB, vec2 pointC,
190 inout vec2 closestPoint, inout float bestTLocal)
191 {
192 const float EPSILON = 1e-3;
193 const float ONE_THIRD = 1.0 / 3.0;
194 bool abEqual = all(lessThan(abs(pointA - pointB), vec2(EPSILON)));
195 bool bcEqual = all(lessThan(abs(pointB - pointC), vec2(EPSILON)));
196 bool acEqual = all(lessThan(abs(pointA - pointC), vec2(EPSILON)));
197
198 if (abEqual && bcEqual) {
199 closestPoint = pointA;
200 return distance(pos, pointA);
201 }
202
203 if (abEqual || acEqual) return SdfLinePartition(pos, pointB, pointC, closestPoint, bestTLocal);
204
205 if (bcEqual) return SdfLinePartition(pos, pointA, pointC, closestPoint, bestTLocal);
206
207 if (abs(dot(normalize(pointB - pointA), normalize(pointC - pointB)) - 1.0) < EPSILON)
208 return SdfLinePartition(pos, pointA, pointC, closestPoint, bestTLocal);
209
210 vec2 a = pointB - pointA;
211 vec2 b = pointA - 2.0 * pointB + pointC;
212 vec2 c = a * 2.0;
213 vec2 d = pointA - pos;
214 float kk = 1.0 / dot(b, b);
215 float kx = kk * dot(a, b);
216 float ky = kk * (2.0 * dot(a, a) + dot(d, b)) * ONE_THIRD;
217 float kz = kk * dot(d, a);
218 float res = 0.0;
219 float sgn = 0.0;
220 float p = ky - kx * kx;
221 float q = kx * (2.0 * kx * kx - 3.0 * ky) + kz;
222 float h = q * q + 4.0 * p * p * p;
223
224 if (h >= 0.0) {
225 h = sqrt(h);
226 vec2 x = 0.5 * (vec2(h, -h) - q);
227 vec2 uv = vec2((x.x > 0.0 ? 1.0 : -1.0) * pow(abs(x.x), ONE_THIRD),
228 (x.y > 0.0 ? 1.0 : -1.0) * pow(abs(x.y), ONE_THIRD));
229 float t = SaturateFloat(uv.x + uv.y - kx) + EPSILON;
230 closestPoint = Bezier(pointA, pointB, pointC, t);
231 bestTLocal = t;
232 vec2 qv = d + (c + b * t) * t;
233 res = dot(qv, qv);
234 sgn = Cross2(c + 2.0 * b * t, qv);
235 return (sgn > 0.0 ? 1.0 : -1.0) * sqrt(res);
236 }
237
238 float z = sqrt(-p);
239 float v = acos(q / (p * z * 2.0)) * ONE_THIRD;
240 float m = cos(v);
241 float n = sin(v) * SQRT3;
242 vec3 t = SaturateVec3(vec3(m + m, -n - m, n - m) * z - kx) + EPSILON;
243 vec2 qx = d + (c + b * t.x) * t.x;
244 float dx = dot(qx, qx);
245 float sx = Cross2(c + 2.0 * b * t.x, qx);
246 vec2 qy = d + (c + b * t.y) * t.y;
247 float dy = dot(qy, qy);
248 float sy = Cross2(c + 2.0 * b * t.y, qy);
249 res = (dx < dy) ? dx : dy;
250 sgn = (dx < dy) ? sx : sy;
251 float bestT = (dx < dy) ? t.x : t.y;
252 closestPoint = Bezier(pointA, pointB, pointC, bestT);
253 bestTLocal = bestT;
254 return (sgn > 0.0 ? 1.0 : -1.0) * sqrt(res);
255 }
256
257 // ================= SDF Polygon =================
258
259 float SdfControlSegment(vec2 p, vec2 pointA, vec2 pointB, vec2 pointC)
260 {
261 return AbsMin(SdfLine(p, pointA, pointB), SdfLine(p, pointB, pointC));
262 }
263
264 float SdfControlPolygon(vec2 p, vec2 controlPoly[capacity], int size,
265 inout vec2 closest[3], inout float closestSegmentIndex)
266 {
267 float minDist = INF;
268 int segmentCount = size / 2;
269
270 // i = 0
271 vec2 pointA = controlPoly[0];
272 vec2 pointB = controlPoly[1];
273 vec2 pointC = controlPoly[2];
274 float d = SdfControlSegment(p, pointA, pointB, pointC);
275 if (abs(d) < abs(minDist)) {
276 minDist = d;
277 closest[0] = pointA;
278 closest[1] = pointB;
279 closest[2] = pointC;
280 closestSegmentIndex = segmentIndex[0];
281 }
282 // i = 1
283 pointA = controlPoly[3]; // iMidPrev = i - 1
284 pointB = controlPoly[4]; // i
285 pointC = controlPoly[5]; // iMid = i + 1
286 d = SdfControlSegment(p, pointA, pointB, pointC);
287 if (abs(d) < abs(minDist)) {
288 minDist = d;
289 closest[0] = pointA;
290 closest[1] = pointB;
291 closest[2] = pointC;
292 closestSegmentIndex = segmentIndex[1];
293 }
294 // i = 2
295 pointA = controlPoly[6]; // iMidPrev = i - 1
296 pointB = controlPoly[7]; // i
297 pointC = controlPoly[8]; // iMid = i + 1
298 d = SdfControlSegment(p, pointA, pointB, pointC);
299 if (abs(d) < abs(minDist)) {
300 minDist = d;
301 closest[0] = pointA;
302 closest[1] = pointB;
303 closest[2] = pointC;
304 closestSegmentIndex = segmentIndex[2];
305 }
306 // i = 3
307 if (size < 9) return minDist; // 9: next i beyond legal size
308 pointA = controlPoly[9]; // iMidPrev = i - 1
309 pointB = controlPoly[10]; // i
310 pointC = controlPoly[11]; // iMid = i + 1
311 d = SdfControlSegment(p, pointA, pointB, pointC);
312 if (abs(d) < abs(minDist)) {
313 minDist = d;
314 closest[0] = pointA;
315 closest[1] = pointB;
316 closest[2] = pointC;
317 closestSegmentIndex = segmentIndex[3];
318 }
319 // i = 4
320 if (size < 12) return minDist; // 12: next i beyond legal size
321 pointA = controlPoly[12]; // iMidPrev = i - 1
322 pointB = controlPoly[13]; // i
323 pointC = controlPoly[14]; // iMid = i + 1
324 d = SdfControlSegment(p, pointA, pointB, pointC);
325 if (abs(d) < abs(minDist)) {
326 minDist = d;
327 closest[0] = pointA;
328 closest[1] = pointB;
329 closest[2] = pointC;
330 closestSegmentIndex = segmentIndex[4];
331 }
332 // i = 5
333 if (size < 15) return minDist; // 15: next i beyond legal size
334 pointA = controlPoly[15]; // iMidPrev = i - 1
335 pointB = controlPoly[16]; // i
336 pointC = controlPoly[17]; // iMid = i + 1
337 d = SdfControlSegment(p, pointA, pointB, pointC);
338 if (abs(d) < abs(minDist)) {
339 minDist = d;
340 closest[0] = pointA;
341 closest[1] = pointB;
342 closest[2] = pointC;
343 closestSegmentIndex = segmentIndex[5];
344 }
345 // i = 6
346 if (size < 18) return minDist; // 18: next i beyond legal size
347 pointA = controlPoly[18]; // iMidPrev = i - 1
348 pointB = controlPoly[19]; // i
349 pointC = controlPoly[20]; // iMid = i + 1
350 d = SdfControlSegment(p, pointA, pointB, pointC);
351 if (abs(d) < abs(minDist)) {
352 minDist = d;
353 closest[0] = pointA;
354 closest[1] = pointB;
355 closest[2] = pointC;
356 closestSegmentIndex = segmentIndex[6];
357 }
358 // i = 7
359 if (size < 21) return minDist; // 21: next i beyond legal size
360 pointA = controlPoly[21]; // iMidPrev = i - 1
361 pointB = controlPoly[22]; // i
362 pointC = controlPoly[23]; // iMid = i + 1
363 d = SdfControlSegment(p, pointA, pointB, pointC);
364 if (abs(d) < abs(minDist)) {
365 minDist = d;
366 closest[0] = pointA;
367 closest[1] = pointB;
368 closest[2] = pointC;
369 closestSegmentIndex = segmentIndex[7];
370 }
371 // i = 8
372 if (size < 24) return minDist; // 24: next i beyond legal size
373 pointA = controlPoly[24]; // iMidPrev = i - 1
374 pointB = controlPoly[25]; // i
375 pointC = controlPoly[26]; // iMid = i + 1
376 d = SdfControlSegment(p, pointA, pointB, pointC);
377 if (abs(d) < abs(minDist)) {
378 minDist = d;
379 closest[0] = pointA;
380 closest[1] = pointB;
381 closest[2] = pointC;
382 closestSegmentIndex = segmentIndex[8];
383 }
384 // i = 9
385 if (size < 27) return minDist; // 27: next i beyond legal size
386 pointA = controlPoly[27]; // iMidPrev = i - 1
387 pointB = controlPoly[28]; // i
388 pointC = controlPoly[29]; // iMid = i + 1
389 d = SdfControlSegment(p, pointA, pointB, pointC);
390 if (abs(d) < abs(minDist)) {
391 minDist = d;
392 closest[0] = pointA;
393 closest[1] = pointB;
394 closest[2] = pointC;
395 closestSegmentIndex = segmentIndex[9];
396 }
397 // i = 10
398 if (size < 30) return minDist; // 30: next i beyond legal size
399 pointA = controlPoly[30]; // iMidPrev = i - 1
400 pointB = controlPoly[31]; // i
401 pointC = controlPoly[32]; // iMid = i + 1
402 d = SdfControlSegment(p, pointA, pointB, pointC);
403 if (abs(d) < abs(minDist)) {
404 minDist = d;
405 closest[0] = pointA;
406 closest[1] = pointB;
407 closest[2] = pointC;
408 closestSegmentIndex = segmentIndex[10];
409 }
410 // i = 11
411 if (size < 33) return minDist; // 33: next i beyond legal size
412 pointA = controlPoly[33]; // iMidPrev = i - 1
413 pointB = controlPoly[34]; // i
414 pointC = controlPoly[35]; // iMid = i + 1
415 d = SdfControlSegment(p, pointA, pointB, pointC);
416 if (abs(d) < abs(minDist)) {
417 minDist = d;
418 closest[0] = pointA;
419 closest[1] = pointB;
420 closest[2] = pointC;
421 closestSegmentIndex = segmentIndex[11];
422 }
423 // i = 12
424 if (size < 36) return minDist; // 36: next i beyond legal size
425 pointA = controlPoly[36]; // iMidPrev = i - 1
426 pointB = controlPoly[37]; // i
427 pointC = controlPoly[38]; // iMid = i + 1
428 d = SdfControlSegment(p, pointA, pointB, pointC);
429 if (abs(d) < abs(minDist)) {
430 minDist = d;
431 closest[0] = pointA;
432 closest[1] = pointB;
433 closest[2] = pointC;
434 closestSegmentIndex = segmentIndex[12];
435 }
436 // i = 13
437 if (size < 39) return minDist; // 39: next i beyond legal size
438 pointA = controlPoly[39]; // iMidPrev = i - 1
439 pointB = controlPoly[40]; // i
440 pointC = controlPoly[41]; // iMid = i + 1
441 d = SdfControlSegment(p, pointA, pointB, pointC);
442 if (abs(d) < abs(minDist)) {
443 minDist = d;
444 closest[0] = pointA;
445 closest[1] = pointB;
446 closest[2] = pointC;
447 closestSegmentIndex = segmentIndex[13];
448 }
449 // i = 14
450 if (size < 42) return minDist; // 42: next i beyond legal size
451 pointA = controlPoly[42]; // iMidPrev = i - 1
452 pointB = controlPoly[43]; // i
453 pointC = controlPoly[44]; // iMid = i + 1
454 d = SdfControlSegment(p, pointA, pointB, pointC);
455 if (abs(d) < abs(minDist)) {
456 minDist = d;
457 closest[0] = pointA;
458 closest[1] = pointB;
459 closest[2] = pointC;
460 closestSegmentIndex = segmentIndex[14];
461 }
462 // i = 15
463 if (size < 45) return minDist; // 45: next i beyond legal size
464 pointA = controlPoly[45]; // iMidPrev = i - 1
465 pointB = controlPoly[46]; // i
466 pointC = controlPoly[47]; // iMid = i + 1
467 d = SdfControlSegment(p, pointA, pointB, pointC);
468 if (abs(d) < abs(minDist)) {
469 minDist = d;
470 closest[0] = pointA;
471 closest[1] = pointB;
472 closest[2] = pointC;
473 closestSegmentIndex = segmentIndex[15];
474 }
475 return minDist;
476 }
477 float SdfBezierShape(vec2 p, vec2 controlPoly[capacity], int controlPolySize,
478 inout vec2 closestPoint, inout float bestTLocal, inout float closestSegmentIndex)
479 {
480 vec2 closest[3];
481 SdfControlPolygon(p, controlPoly, controlPolySize, closest, closestSegmentIndex);
482 return SdfBezier(p, closest[0], closest[1], closest[2], closestPoint, bestTLocal);
483 }
484 vec2 EncodeFloat(float x)
485 {
486 x = clamp(x, 0.0, 1.0);
487 float hi = floor(x * 255.0) / 255.0; // 255.0 = maximum value representable in 8-bit channel
488 float lo = fract(x * 255.0); // 255.0 = maximum value representable in 8-bit channel
489 return vec2(hi, lo);
490 }
491 vec4 main(vec2 fragCoord)
492 {
493 int size = int(count);
494 float segmentCount = 0.5 * count; // 0.5: half amount of the control point count
495 vec2 p = (2.0 * fragCoord - iResolution.xy) / iResolution.y; // 2.0: normalized screen coordinates
496 vec2 closestPoint = vec2(0.0);
497 float tLocal = 0.0;
498 float closestSegmentIndex = 0.0;
499 float sdf = SdfBezierShape(p, controlPoints, size, closestPoint, tLocal, closestSegmentIndex);
500 vec2 encodedSdf = EncodeFloat(abs(sdf));
501 float tGlobal = (closestSegmentIndex + tLocal) / segmentCount;
502 return vec4(encodedSdf, tGlobal, 1.0);
503 }
504 )";
505
506 static constexpr char BLEND_IMG_PROG[] = R"(
507 uniform shader precalculationImage;
508 uniform shader image1;
509 uniform shader image2;
510 uniform float weight1;
511 uniform float weight2;
512 uniform float blurRadius;
513 float DecodeFloat(vec2 rg)
514 {
515 return rg.x + rg.y / 255.0; // 255.0 = maximum value representable in 8-bit channel
516 }
517 vec4 main(vec2 fragCoord)
518 {
519 float sdf = DecodeFloat(precalculationImage.eval(fragCoord).rg);
520 vec4 c1 = image1.eval(fragCoord).rgba;
521 vec4 c2 = image2.eval(fragCoord).rgba;
522 float totalWeight = (weight1 + weight2 < 1e-5) ? 1.0 : weight1 + weight2;
523 float contourWeight = exp(-sdf * 20.0); // 20.0: the greater, the thinner core
524 contourWeight *= blurRadius / 50.0; // 50.0: default blur radius
525 if (precalculationImage.eval(fragCoord).a < 0.5) { // 0.5: discard the transparent pixels
526 contourWeight = 0.0;
527 }
528 return c1 + c2 * weight2 * weight2 / (totalWeight * weight1) * contourWeight;
529 }
530 )";
531
ConvertUVToNDC(const std::vector<Vector2f> & uvPoints,float width,float height)532 std::vector<Vector2f> ConvertUVToNDC(const std::vector<Vector2f>& uvPoints, float width, float height)
533 {
534 if (height < 1) {
535 return {};
536 }
537 std::vector<Vector2f> ndcPoints;
538 ndcPoints.reserve(uvPoints.size());
539 float aspect = static_cast<float>(width) / static_cast<float>(height);
540
541 for (const auto& uv : uvPoints) {
542 float ndcX = (uv[0] * 2.0f - 1.0f) * aspect;
543 float ndcY = uv[1] * 2.0f - 1.0f;
544 ndcPoints.emplace_back(ndcX, ndcY);
545 }
546
547 return ndcPoints;
548 }
549
ConvertPointsTo(const std::vector<Vector2f> & in,std::vector<float> & out)550 void ConvertPointsTo(const std::vector<Vector2f>& in, std::vector<float>& out)
551 {
552 out.clear();
553 for (auto& p : in) {
554 out.push_back(p[0]);
555 out.push_back(p[1]);
556 }
557 }
558
CalHash(const std::vector<Vector2f> & in)559 uint32_t CalHash(const std::vector<Vector2f>& in)
560 {
561 #ifdef USE_M133_SKIA
562 const auto hashFunc = SkChecksum::Hash32;
563 #else
564 const auto hashFunc = SkOpts::hash;
565 #endif
566 uint32_t hashOut = 0;
567 for (auto& p : in) {
568 hashOut = hashFunc(&p, sizeof(p), hashOut);
569 }
570 return hashOut;
571 }
572
FEqual(float a,float b)573 bool FEqual(float a, float b)
574 {
575 return std::abs(a - b) <= std::numeric_limits<float>::epsilon();
576 }
577
intersectBBox(const Box4f & a,const Box4f & b)578 bool intersectBBox(const Box4f& a, const Box4f& b)
579 {
580 return !(a[XMAX_I] < b[XMIN_I]
581 || a[XMIN_I] > b[XMAX_I]
582 || a[YMAX_I] < b[YMIN_I] || a[YMIN_I] > b[YMAX_I]);
583 }
584
BBoxOverLap(const Box4f & a,const Box4f & b)585 float BBoxOverLap(const Box4f& a, const Box4f& b)
586 {
587 float overlapXmin = std::max(a[0], b[0]);
588 float overlapXmax = std::min(a[1], b[1]);
589 float overlapYmin = std::max(a[2], b[2]);
590 float overlapYmax = std::min(a[3], b[3]);
591 float overlapArea = (overlapXmax - overlapXmin) * (overlapYmax - overlapYmin);
592 float areaA = (a[1] - a[0]) * (a[3] - a[2]);
593 float esp = 0.001;
594 if (areaA < esp) {
595 return esp;
596 } else {
597 return overlapArea / areaA;
598 }
599 }
600 } // anonymous namespace
601
GEContourDiagonalFlowLightShader()602 GEContourDiagonalFlowLightShader::GEContourDiagonalFlowLightShader() {}
603
GEContourDiagonalFlowLightShader(GEContentDiagonalFlowLightShaderParams & param)604 GEContourDiagonalFlowLightShader::GEContourDiagonalFlowLightShader(GEContentDiagonalFlowLightShaderParams& param)
605 {
606 contourDiagonalFlowLightParams_ = param;
607 Drawing::GEKawaseBlurShaderFilterParams blurParas{contourDiagonalFlowLightParams_.radius_};
608 blurShader_ = std::make_shared<GEKawaseBlurShaderFilter>(blurParas);
609 }
610
CreateFlowLightShader(GEContentDiagonalFlowLightShaderParams & param)611 std::shared_ptr<GEContourDiagonalFlowLightShader> GEContourDiagonalFlowLightShader::CreateFlowLightShader(
612 GEContentDiagonalFlowLightShaderParams& param)
613 {
614 std::shared_ptr<GEContourDiagonalFlowLightShader> contourDiagonalFlowLightShader =
615 std::make_shared<GEContourDiagonalFlowLightShader>(param);
616 return contourDiagonalFlowLightShader;
617 }
618
MakeDrawingShader(const Drawing::Rect & rect,float progress)619 void GEContourDiagonalFlowLightShader::MakeDrawingShader(const Drawing::Rect& rect, float progress) {}
620
GetFlowLightPrecalBuilder()621 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEContourDiagonalFlowLightShader::GetFlowLightPrecalBuilder()
622 {
623 thread_local std::shared_ptr<Drawing::RuntimeEffect> contourDiagonalFlowLightShaderEffectPrecalculation_ = nullptr;
624 if (contourDiagonalFlowLightShaderEffectPrecalculation_ == nullptr) {
625 contourDiagonalFlowLightShaderEffectPrecalculation_ = Drawing::RuntimeEffect::CreateForShader(
626 PRECALCULATION_PROG);
627 }
628
629 if (contourDiagonalFlowLightShaderEffectPrecalculation_ == nullptr) {
630 GE_LOGE("GEContourDiagonalFlowLightShader contourDiagonalFlowLightShaderEffectPrecalculation_ is nullptr.");
631 return nullptr;
632 }
633
634 return std::make_shared<Drawing::RuntimeShaderBuilder>(contourDiagonalFlowLightShaderEffectPrecalculation_);
635 }
636
FlowLightConvertBuilder()637 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEContourDiagonalFlowLightShader::FlowLightConvertBuilder()
638 {
639 thread_local std::shared_ptr<Drawing::RuntimeEffect> convertShader = nullptr;
640 if (convertShader == nullptr) {
641 convertShader = Drawing::RuntimeEffect::CreateForShader(
642 PRECALCULATION_PROG); // replace CONVERT_IMG_PROG
643 }
644
645 if (convertShader == nullptr) {
646 GE_LOGE("GEContourDiagonalFlowLightShader convertShader is nullptr.");
647 return nullptr;
648 }
649
650 return std::make_shared<Drawing::RuntimeShaderBuilder>(convertShader);
651 }
652
Preprocess(Drawing::Canvas & canvas,const Drawing::Rect & rect)653 void GEContourDiagonalFlowLightShader::Preprocess(Drawing::Canvas& canvas, const Drawing::Rect& rect)
654 {
655 if (contourDiagonalFlowLightParams_.contour_.size() < MIN_NUM) {
656 GE_LOGW("GEContourDiagonalFlowLightShader less point %{public}zu",
657 contourDiagonalFlowLightParams_.contour_.size());
658 cacheAnyPtr_ = nullptr;
659 return;
660 }
661 pointCnt_ = contourDiagonalFlowLightParams_.contour_.size();
662 auto inHash = CalHash(contourDiagonalFlowLightParams_.contour_);
663 float inRadius = contourDiagonalFlowLightParams_.radius_;
664 if (cacheAnyPtr_ == nullptr || inHash != std::any_cast<CacheDataType>(*cacheAnyPtr_).hash ||
665 !FEqual(inRadius, std::any_cast<CacheDataType>(*cacheAnyPtr_).blurRadius)) {
666 auto ndcPoints = ConvertUVToNDC(contourDiagonalFlowLightParams_.contour_,
667 static_cast<float>(rect.GetWidth()), static_cast<float>(rect.GetHeight()));
668 CreateSurfaceAndCanvas(canvas, rect);
669 if (offscreenSurface_ == nullptr || offscreenCanvas_ == nullptr) {
670 GE_LOGW("GEContourDiagonalFlowLightShader create surface or canvas failed");
671 cacheAnyPtr_ = nullptr;
672 return;
673 }
674 ConvertPointsTo(ndcPoints, controlPoints_);
675 numCurves_ = pointCnt_ / 2; // one segment need 2 point
676 capacity_ = pointCnt_;
677 controlPoints_.resize(pointCnt_ * POSITION_CHANNEL);
678
679 AutoPartitionCal(canvas, rect);
680
681 CacheDataType cacheData;
682 cacheData.hash = inHash;
683 cacheData.blurRadius = inRadius;
684
685 auto cacheImg = offscreenSurface_->GetImageSnapshot();
686 if (cacheImg) {
687 cacheData.precalculationImg = cacheImg;
688 cacheAnyPtr_ = std::make_shared<std::any>(std::make_any<CacheDataType>(cacheData));
689 }
690 }
691 }
692
AutoPartitionCal(Drawing::Canvas & canvas,const Drawing::Rect & rect)693 void GEContourDiagonalFlowLightShader::AutoPartitionCal(Drawing::Canvas& canvas, const Drawing::Rect& rect)
694 {
695 if (offscreenCanvas_ == nullptr) {
696 GE_LOGW("GEContourDiagonalFlowLightShader::AutoPartitionCal offscreenCanvas canvas failed");
697 return;
698 }
699 float blurRadiusBound = 2.0f * // convert pixel scale to ndc scale
700 contourDiagonalFlowLightParams_.radius_ / (FEqual(rect.GetHeight(), 0.0f) ? 1.0f : rect.GetHeight());
701 float maxThickness = 0.05 + blurRadiusBound; // 0.05: max thickness of the curve
702 AutoGridPartition(rect.GetWidth(), rect.GetHeight(), maxThickness);
703 // gpu cal
704 for (int i = 0; i < static_cast<int>(curvesInGrid_.size()); i++) {
705 if (curvesInGrid_[i].first.size() > 0) {
706 Box4f area = curvesInGrid_[i].second;
707 const Drawing::Rect rectN = Drawing::Rect(area[0],
708 area[2], area[1], area[3]);
709 PreCalculateRegion(*offscreenCanvas_, i, rect, rectN);
710 }
711 }
712 }
713
AutoGridPartition(int width,int height,float maxThickness)714 void GEContourDiagonalFlowLightShader::AutoGridPartition(int width, int height, float maxThickness)
715 {
716 curvesInGrid_.clear();
717 // calculate the bounding box of all curves
718 std::vector<Box4f> curveBBoxes;
719 Box4f canvasBBox;
720 ComputeAllCurveBoundingBoxes(width, height, maxThickness, canvasBBox, curveBBoxes);
721 // init workQueue
722 std::queue<Grid> workQueue;
723 InitializeWorkQueue(canvasBBox, curveBBoxes, workQueue);
724 // grid Partition
725 while (!workQueue.empty()) {
726 Grid current = workQueue.front();
727 workQueue.pop();
728 // cal grid size
729 float w = current.bbox[1] - current.bbox[0];
730 float h = current.bbox[3] - current.bbox[2];
731 // check is need to split
732 bool needsSplit = (static_cast<int>(current.curveIndices.size()) > MAX_CURVES_PER_GRID)
733 && (w > MIN_GRID_SIZE && h > MIN_GRID_SIZE);
734 if (needsSplit) {
735 SplitGrid(current, curveBBoxes, workQueue, MIN_GRID_SIZE);
736 } else {
737 // if grid size < MIN_GRID_SIZE and curves > MAX_CURVES_PER_GRID, drop the low-weight curves
738 bool shouldSelectTopCurves = (w <= MIN_GRID_SIZE && h <= MIN_GRID_SIZE);
739 ProcessFinalGrid(current, curveBBoxes, shouldSelectTopCurves);
740 }
741 }
742 }
743
SplitGrid(const Grid & current,const std::vector<Box4f> & curveBBoxes,std::queue<Grid> & workQueue,float minGridSize)744 void GEContourDiagonalFlowLightShader::SplitGrid(
745 const Grid& current, const std::vector<Box4f>& curveBBoxes,
746 std::queue<Grid>& workQueue, float minGridSize)
747 {
748 // partition grid - quadtree
749 float midX = (current.bbox[0] + current.bbox[1]) * 0.5f;
750 float midY = (current.bbox[2] + current.bbox[3]) * 0.5f;
751 Box4f quadrants[4] = {
752 {current.bbox[0], midX, current.bbox[2], midY}, // Top-Left
753 {midX, current.bbox[1], current.bbox[2], midY}, // Top-Right
754 {current.bbox[0], midX, midY, current.bbox[3]}, // Bottom-Left
755 {midX, current.bbox[1], midY, current.bbox[3]} // Bottom-Right
756 };
757 for (const auto& quad : quadrants) {
758 Grid child{quad, {}};
759 // find intersecting curves
760 for (int idx : current.curveIndices) {
761 if (intersectBBox(child.bbox, curveBBoxes[idx])) {
762 child.curveIndices.push_back(idx);
763 }
764 }
765 // add node to workqueue
766 if (!child.curveIndices.empty()) {
767 workQueue.push(child);
768 }
769 }
770 }
771
ComputeAllCurveBoundingBoxes(int width,int height,float maxThickness,Box4f & canvasBBox,std::vector<Box4f> & curveBBoxes)772 void GEContourDiagonalFlowLightShader::ComputeAllCurveBoundingBoxes(
773 int width, int height, float maxThickness, Box4f& canvasBBox,
774 std::vector<Box4f>& curveBBoxes)
775 {
776 curveBBoxes.clear();
777 curveBBoxes.reserve(numCurves_);
778 // calculate the Minimum Bounding Box of the contour
779 canvasBBox = {
780 static_cast<float>(width),
781 0.0f,
782 static_cast<float>(height),
783 0.0f,
784 };
785 for (int i = 0; i < numCurves_; ++i) {
786 Box4f bbox = ComputeCurveBoundingBox(i, maxThickness, width, height);
787 curveBBoxes.push_back(bbox);
788 canvasBBox[XMIN_I] = std::min(bbox[XMIN_I], canvasBBox[XMIN_I]);
789 canvasBBox[XMAX_I] = std::max(bbox[XMAX_I], canvasBBox[XMAX_I]);
790 canvasBBox[YMIN_I] = std::min(bbox[YMIN_I], canvasBBox[YMIN_I]);
791 canvasBBox[YMAX_I] = std::max(bbox[YMAX_I], canvasBBox[YMAX_I]);
792 }
793 }
794
ComputeCurveBoundingBox(size_t curveIndex,float maxThickness,int width,int height)795 Box4f GEContourDiagonalFlowLightShader::ComputeCurveBoundingBox(
796 size_t curveIndex, float maxThickness, int width, int height)
797 {
798 float x0 = controlPoints_[4 * curveIndex]; // startPoint x
799 float y0 = controlPoints_[4 * curveIndex + 1]; // startPoint y
800 float cx = controlPoints_[4 * curveIndex + 2]; // controlPoint x
801 float cy = controlPoints_[4 * curveIndex + 3]; // controlPoint y
802 float x1 = controlPoints_[(4 * curveIndex + 4) % controlPoints_.size()]; // endPoint x
803 float y1 = controlPoints_[(4 * curveIndex + 5) % controlPoints_.size()]; // endPoint y
804 float minX = std::min({x0, cx, x1}) - maxThickness;
805 float maxX = std::max({x0, cx, x1}) + maxThickness;
806 float minY = std::min({y0, cy, y1}) - maxThickness;
807 float maxY = std::max({y0, cy, y1}) + maxThickness;
808 // map ndc to [0, 1]
809 float aspect = width / (FEqual(height, 0.0f) ? 1.0f : height);
810 minX = std::floor((minX / aspect + 1.0f) / 2.0f * width);
811 maxX = std::ceil((maxX / aspect + 1.0f) / 2.0f * width);
812 minY = std::floor((minY + 1.0f) / 2.0f * height);
813 maxY = std::ceil((maxY + 1.0f) / 2.0f * height);
814 // check the curve is out of the screen, find the intersection
815 minX = std::max(minX, 0.0f);
816 maxX = std::min(maxX, static_cast<float>(width));
817 minY = std::max(minY, 0.0f);
818 maxY = std::min(maxY, static_cast<float>(height));
819 return {minX, maxX, minY, maxY};
820 }
821
InitializeWorkQueue(const Box4f & canvasBBox,const std::vector<Box4f> & curveBBoxes,std::queue<Grid> & workQueue)822 void GEContourDiagonalFlowLightShader::InitializeWorkQueue(
823 const Box4f& canvasBBox, const std::vector<Box4f>& curveBBoxes,
824 std::queue<Grid>& workQueue)
825 {
826 // init root grid
827 std::vector<int> initialCurves;
828 for (int i = 0; i < numCurves_; ++i) {
829 if (intersectBBox(canvasBBox, curveBBoxes[i])) {
830 initialCurves.push_back(i);
831 }
832 }
833 workQueue.push({canvasBBox, initialCurves});
834 }
835
ProcessFinalGrid(Grid & current,const std::vector<Box4f> & curveBBoxes,bool shouldSelectTopCurves)836 void GEContourDiagonalFlowLightShader::ProcessFinalGrid(
837 Grid& current, const std::vector<Box4f>& curveBBoxes, bool shouldSelectTopCurves)
838 {
839 Grid processedGrid = current;
840
841 if (shouldSelectTopCurves && processedGrid.curveIndices.size() > MAX_CURVES_PER_GRID) {
842 processedGrid.curveIndices = SelectTopCurves(current, curveBBoxes, MAX_CURVES_PER_GRID);
843 }
844 std::vector<float> gridCurves;
845 std::vector<float> inOrderSeg;
846 const int slidingWindowLen = 4; // curve 3 point(6 value), slidingWindowLen is 4
847 for (int idx : processedGrid.curveIndices) {
848 gridCurves.push_back(controlPoints_[slidingWindowLen * idx]); // start point x
849 gridCurves.push_back(controlPoints_[slidingWindowLen * idx + 1]); // start point y
850 gridCurves.push_back(controlPoints_[slidingWindowLen * idx + 2]); // 2:control point x
851 gridCurves.push_back(controlPoints_[slidingWindowLen * idx + 3]); // 3:control point y
852 gridCurves.push_back(controlPoints_[(slidingWindowLen * idx + 4) % controlPoints_.size()]); // 4:end point x
853 gridCurves.push_back(controlPoints_[(slidingWindowLen * idx + 5) % controlPoints_.size()]); // 5:end point y
854
855 inOrderSeg.push_back(static_cast<float>(idx));
856 }
857 curvesInGrid_.push_back(std::make_pair(gridCurves, processedGrid.bbox));
858 segmentIndex_.push_back(inOrderSeg);
859 }
860
SelectTopCurves(const Grid & current,const std::vector<Box4f> & curveBBoxes,size_t topK)861 std::vector<int> GEContourDiagonalFlowLightShader::SelectTopCurves(
862 const Grid& current, const std::vector<Box4f>& curveBBoxes, size_t topK)
863 {
864 std::vector<std::pair<float, int>> iouIndexPairs;
865 iouIndexPairs.reserve(current.curveIndices.size());
866
867 for (int idx : current.curveIndices) {
868 iouIndexPairs.emplace_back(BBoxOverLap(curveBBoxes[idx], current.bbox), idx);
869 }
870 const size_t safeTopK = std::min<size_t>(topK, iouIndexPairs.size());
871
872 // Topk Sort
873 std::partial_sort(
874 iouIndexPairs.begin(),
875 iouIndexPairs.begin() + safeTopK,
876 iouIndexPairs.end(),
877 [](const auto& a,
878 const auto& b) {
879 return a.first > b.first;
880 });
881
882 std::vector<int> topIndices;
883 topIndices.reserve(safeTopK);
884
885 for (size_t i = 0; i < topK; i++) {
886 topIndices.push_back(iouIndexPairs[i].second);
887 }
888 return topIndices;
889 }
890
GetContourDiagonalFlowLightBuilder()891 std::shared_ptr<Drawing::RuntimeShaderBuilder> GEContourDiagonalFlowLightShader::GetContourDiagonalFlowLightBuilder()
892 {
893 thread_local std::shared_ptr<Drawing::RuntimeEffect> contourDiagonalFlowLightShaderEffect_ = nullptr;
894 if (contourDiagonalFlowLightShaderEffect_ == nullptr) {
895 contourDiagonalFlowLightShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(FLOW_LIGHT_PROG);
896 }
897
898 if (contourDiagonalFlowLightShaderEffect_ == nullptr) {
899 GE_LOGE("GEContourDiagonalFlowLightShader contourDiagonalFlowLightShaderEffect_ is nullptr.");
900 return nullptr;
901 }
902
903 return std::make_shared<Drawing::RuntimeShaderBuilder>(contourDiagonalFlowLightShaderEffect_);
904 }
905
DrawRuntimeShader(Drawing::Canvas & canvas,const Drawing::Rect & rect)906 std::shared_ptr<Drawing::Image> GEContourDiagonalFlowLightShader::DrawRuntimeShader(Drawing::Canvas& canvas,
907 const Drawing::Rect& rect)
908 {
909 if (cacheAnyPtr_ == nullptr) {
910 GE_LOGW("GEContourDiagonalFlowLightShader DrawRuntimeShader cache is nullptr.");
911 return nullptr;
912 }
913 auto precalculationImage = std::any_cast<CacheDataType>(*cacheAnyPtr_).precalculationImg;
914 if (precalculationImage == nullptr) {
915 cacheAnyPtr_ = nullptr;
916 GE_LOGW("GEContourDiagonalFlowLightShader DrawRuntimeShader cache img is nullptr.");
917 return nullptr;
918 }
919 auto width = rect.GetWidth();
920 auto height = rect.GetHeight();
921 builder_ = GetContourDiagonalFlowLightBuilder();
922 Drawing::Matrix matrix;
923 auto precalculationShader = Drawing::ShaderEffect::CreateImageShader(*precalculationImage, Drawing::TileMode::CLAMP,
924 Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
925 const float defaultLineLength = 0.3f;
926 float line1Length = (contourDiagonalFlowLightParams_.line1Length_ < 0.0f ||
927 FEqual(contourDiagonalFlowLightParams_.line1Length_, 0.0f)) ?
928 defaultLineLength : contourDiagonalFlowLightParams_.line1Length_;
929 float line2Length = (contourDiagonalFlowLightParams_.line2Length_ < 0.0f ||
930 FEqual(contourDiagonalFlowLightParams_.line2Length_, 0.0f)) ?
931 defaultLineLength : contourDiagonalFlowLightParams_.line2Length_;
932 builder_->SetChild("precalculationImage", precalculationShader);
933 builder_->SetUniform("iResolution", width, height);
934 builder_->SetUniform("line1Start", contourDiagonalFlowLightParams_.line1Start_);
935 builder_->SetUniform("line1Length", std::clamp(line1Length, 0.0f, 1.0f));
936 builder_->SetUniform("line1Color", std::clamp(contourDiagonalFlowLightParams_.line1Color_[NUM0], 0.0f, 1.0f),
937 std::clamp(contourDiagonalFlowLightParams_.line1Color_[NUM1], 0.0f, 1.0f),
938 std::clamp(contourDiagonalFlowLightParams_.line1Color_[NUM2], 0.0f, 1.0f));
939 builder_->SetUniform("line2Start", contourDiagonalFlowLightParams_.line2Start_);
940 builder_->SetUniform("line2Length", std::clamp(line2Length, 0.0f, 1.0f));
941 builder_->SetUniform("line2Color", std::clamp(contourDiagonalFlowLightParams_.line2Color_[NUM0], 0.0f, 1.0f),
942 std::clamp(contourDiagonalFlowLightParams_.line2Color_[NUM1], 0.0f, 1.0f),
943 std::clamp(contourDiagonalFlowLightParams_.line2Color_[NUM2], 0.0f, 1.0f));
944 builder_->SetUniform("lineThickness", std::clamp(contourDiagonalFlowLightParams_.thickness_, 0.0f, 1.0f));
945 Drawing::ImageInfo imageInfo(rect.GetWidth(), rect.GetHeight(),
946 Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE);
947 auto img = builder_->MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false);
948 return img;
949 }
950
PreCalculateRegion(Drawing::Canvas & canvas,int gridIndex,const Drawing::Rect & wholeRect,const Drawing::Rect & rect)951 void GEContourDiagonalFlowLightShader::PreCalculateRegion(Drawing::Canvas& canvas, int gridIndex,
952 const Drawing::Rect& wholeRect, const Drawing::Rect& rect)
953 {
954 int curveValueCount = 6;
955 curvesInGrid_[gridIndex].first.resize(MAX_CURVES_PER_GRID * curveValueCount, -2.0); // -2.0:unused control points
956 segmentIndex_[gridIndex].resize(MAX_CURVES_PER_GRID, 0.0);
957 auto builder = GetFlowLightPrecalBuilder();
958 builder->SetUniform("iResolution", wholeRect.GetWidth(), wholeRect.GetHeight());
959 builder->SetUniform("count", static_cast<float>(pointCnt_));
960 builder->SetUniform("controlPoints", curvesInGrid_[gridIndex].first.data(), curvesInGrid_[gridIndex].first.size());
961 builder->SetUniform("segmentIndex", segmentIndex_[gridIndex].data(), segmentIndex_[gridIndex].size());
962 auto contourDiagonalFlowLightShader = builder->MakeShader(nullptr, false);
963 if (contourDiagonalFlowLightShader == nullptr) {
964 GE_LOGE("GEContourDiagonalFlowLightShader::PreCalculateRegion contourDiagonalFlowLightShader is nullptr.");
965 }
966 Drawing::Brush brush;
967 brush.SetShaderEffect(contourDiagonalFlowLightShader);
968 canvas.AttachBrush(brush);
969 canvas.DrawRect(rect);
970 canvas.DetachBrush();
971 }
972
LoopAllCurvesInBatches(Drawing::Canvas & mainCanvas,Drawing::Canvas & canvas,int gridIndex,const Drawing::Rect & wholeRect,const Drawing::Rect & rect)973 std::shared_ptr<Drawing::Image> GEContourDiagonalFlowLightShader::LoopAllCurvesInBatches(
974 Drawing::Canvas& mainCanvas, Drawing::Canvas& canvas, int gridIndex, const Drawing::Rect& wholeRect,
975 const Drawing::Rect& rect)
976 {
977 if (!wholeRect.IsValid() || !rect.IsValid()) {
978 GE_LOGW("GEContourDiagonalFlowLightShader::LoopAllCurvesInBatches rect is invalid");
979 return nullptr;
980 }
981 auto sdfImg = CreateImg(mainCanvas, wholeRect);
982 auto builder = GetFlowLightPrecalBuilder(); // replace FlowLightPrecalBuilderForMoreCurves()
983 if (builder == nullptr || offscreenSurface_ == nullptr) {
984 GE_LOGW("GEContourDiagonalFlowLightShader::LoopAllCurvesInBatches builder or offscreenSurface is nullptr");
985 return nullptr;
986 }
987 constexpr size_t curveValueCount = 6; // one curve have 3 point - 6 float
988 auto perSubCurveSize = static_cast<size_t>(MAX_CURVES_PER_GRID) * curveValueCount; // 16 * 6
989 auto subCurveCnt = curvesInGrid_[gridIndex].first.size() / perSubCurveSize + 1;
990
991 ResizeCurvesData(gridIndex, subCurveCnt, perSubCurveSize);
992
993 Drawing::Matrix matrix;
994 for (size_t i = 0; i < subCurveCnt; i++) {
995 std::vector<float> curvesPoints(curvesInGrid_[gridIndex].first.begin() + i * perSubCurveSize,
996 curvesInGrid_[gridIndex].first.begin() + (i + 1) * perSubCurveSize);
997 std::vector<float> curvesIndex(segmentIndex_[gridIndex].begin() + i * MAX_CURVES_PER_GRID,
998 segmentIndex_[gridIndex].begin() + (i + 1) * MAX_CURVES_PER_GRID);
999 if (sdfImg == nullptr) {
1000 GE_LOGW("GEContourDiagonalFlowLightShader::LoopAllCurvesInBatches sdfImg is nullptr");
1001 return nullptr;
1002 }
1003 auto sdfImgShader = Drawing::ShaderEffect::CreateImageShader(*sdfImg, Drawing::TileMode::CLAMP,
1004 Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::NEAREST), matrix);
1005 builder->SetChild("loopImage", sdfImgShader);
1006 builder->SetUniform("iResolution", wholeRect.GetWidth(), wholeRect.GetHeight());
1007 builder->SetUniform("count", static_cast<float>(pointCnt_));
1008 builder->SetUniform("controlPoints", curvesPoints.data(), curvesPoints.size());
1009 builder->SetUniform("segmentIndex", curvesIndex.data(), curvesIndex.size());
1010 auto flowLightShader = builder->MakeShader(nullptr, false);
1011 if (flowLightShader == nullptr) {
1012 GE_LOGE("GEContourDiagonalFlowLightShader::LoopAllCurvesInBatches FlowLightShader is nullptr.");
1013 return nullptr;
1014 }
1015 Drawing::Brush brush;
1016 brush.SetShaderEffect(flowLightShader);
1017 if (i != subCurveCnt - 1) {
1018 sdfImg = CreateDrawImg(mainCanvas, wholeRect, rect, brush);
1019 } else {
1020 canvas.AttachBrush(brush);
1021 canvas.DrawRect(rect);
1022 canvas.DetachBrush();
1023 sdfImg = offscreenSurface_->GetImageSnapshot();
1024 }
1025 }
1026 return sdfImg;
1027 }
1028
ResizeCurvesData(int gridIndex,size_t subCurveCnt,size_t perSubCurveSize)1029 void GEContourDiagonalFlowLightShader::ResizeCurvesData(int gridIndex, size_t subCurveCnt, size_t perSubCurveSize)
1030 {
1031 curvesInGrid_[gridIndex].first.resize(subCurveCnt * perSubCurveSize, -5.0f); // unused Points fill invalid value
1032 segmentIndex_[gridIndex].resize(subCurveCnt * MAX_CURVES_PER_GRID, 0.0f);
1033 }
1034
PreCalculateRegionForMoreCurves(Drawing::Canvas & mainCanvas,Drawing::Canvas & canvas,int gridIndex,const Drawing::Rect & wholeRect,const Drawing::Rect & rect)1035 void GEContourDiagonalFlowLightShader::PreCalculateRegionForMoreCurves(
1036 Drawing::Canvas& mainCanvas, Drawing::Canvas& canvas, int gridIndex,
1037 const Drawing::Rect& wholeRect, const Drawing::Rect& rect)
1038 {
1039 auto sdfImg = LoopAllCurvesInBatches(mainCanvas, canvas, gridIndex, wholeRect, rect);
1040 ConvertImg(canvas, rect, sdfImg);
1041 }
1042
ConvertImg(Drawing::Canvas & canvas,const Drawing::Rect & rect,std::shared_ptr<Drawing::Image> sdfImg)1043 void GEContourDiagonalFlowLightShader::ConvertImg(Drawing::Canvas& canvas, const Drawing::Rect& rect,
1044 std::shared_ptr<Drawing::Image> sdfImg)
1045 {
1046 if (sdfImg == nullptr) {
1047 GE_LOGE("GEContourDiagonalFlowLightShader::ConvertSDFImg sdfImg is nullptr.");
1048 return;
1049 }
1050 auto convertBuilder = FlowLightConvertBuilder();
1051 if (convertBuilder == nullptr) {
1052 GE_LOGE("GEContourDiagonalFlowLightShader::ConvertSDFImg convertBuilder is nullptr.");
1053 return;
1054 }
1055 Drawing::Matrix matrix;
1056 auto sdfImgShader = Drawing::ShaderEffect::CreateImageShader(*sdfImg,
1057 Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::NEAREST),
1058 matrix);
1059 convertBuilder->SetChild("loopImage", sdfImgShader);
1060 convertBuilder->SetUniform("count", static_cast<float>(pointCnt_));
1061 auto convertShader = convertBuilder->MakeShader(nullptr, false);
1062 if (convertShader == nullptr) {
1063 GE_LOGE("GEContourDiagonalFlowLightShader::ConvertSDFImg convertShader is nullptr.");
1064 return;
1065 }
1066 Drawing::Brush brush;
1067 brush.SetShaderEffect(convertShader);
1068 canvas.AttachBrush(brush);
1069 canvas.DrawRect(rect);
1070 canvas.DetachBrush();
1071 }
1072
CreateSurfaceAndCanvas(Drawing::Canvas & canvas,const Drawing::Rect & rect)1073 void GEContourDiagonalFlowLightShader::CreateSurfaceAndCanvas(Drawing::Canvas& canvas, const Drawing::Rect& rect)
1074 {
1075 auto surface = canvas.GetSurface();
1076 if (surface == nullptr) {
1077 GE_LOGW("GEContourDiagonalFlowLightShader::ProcessImage surface is invalid");
1078 return;
1079 }
1080 offscreenSurface_ = surface->MakeSurface(rect.GetWidth(),
1081 rect.GetHeight());
1082 if (offscreenSurface_ == nullptr) {
1083 GE_LOGW("GEContourDiagonalFlowLightShader::ProcessImage offscreenSurface is invalid");
1084 return;
1085 }
1086 offscreenCanvas_ = offscreenSurface_->GetCanvas();
1087 if (offscreenCanvas_ == nullptr) {
1088 GE_LOGW("GEContourDiagonalFlowLightShader::ProcessImage offscreenCanvas is invalid");
1089 }
1090 }
1091
CreateImg(Drawing::Canvas & canvas,const Drawing::Rect & rect)1092 std::shared_ptr<Drawing::Image> GEContourDiagonalFlowLightShader::CreateImg(Drawing::Canvas& canvas,
1093 const Drawing::Rect& rect)
1094 {
1095 if (!rect.IsValid()) {
1096 GE_LOGW("GEContourDiagonalFlowLightShader::CreateImg rect is invalid");
1097 return nullptr;
1098 }
1099 auto surface = canvas.GetSurface();
1100 if (surface == nullptr) {
1101 GE_LOGW("GEContourDiagonalFlowLightShader::CreateImg surface is invalid");
1102 return nullptr;
1103 }
1104 auto offscreenSurface = surface->MakeSurface(rect.GetWidth(), rect.GetHeight());
1105 if (offscreenSurface == nullptr) {
1106 GE_LOGW("GEContourDiagonalFlowLightShader::CreateImg offscreenSurface is invalid");
1107 return nullptr;
1108 }
1109 auto offscreenCanvas = offscreenSurface->GetCanvas();
1110 if (offscreenCanvas == nullptr) {
1111 GE_LOGW("GEContourDiagonalFlowLightShader::CreateImg offscreenCanvas is invalid");
1112 return nullptr;
1113 }
1114 Drawing::Brush brush;
1115 brush.SetColor(0xFFFFFFFF);
1116 offscreenCanvas->AttachBrush(brush);
1117 offscreenCanvas->DrawRect(Drawing::Rect{0, 0, rect.GetWidth(), rect.GetHeight()});
1118 offscreenCanvas->DetachBrush();
1119 return offscreenSurface->GetImageSnapshot();
1120 }
1121
CreateDrawImg(Drawing::Canvas & canvas,const Drawing::Rect & wholeRect,const Drawing::Rect & rect,const Drawing::Brush & brush)1122 std::shared_ptr<Drawing::Image> GEContourDiagonalFlowLightShader::CreateDrawImg(Drawing::Canvas& canvas,
1123 const Drawing::Rect& wholeRect, const Drawing::Rect& rect, const Drawing::Brush& brush)
1124 {
1125 if (!rect.IsValid()) {
1126 GE_LOGW("GEContourDiagonalFlowLightShader::CreateDrawImg rect is invalid");
1127 return nullptr;
1128 }
1129 auto surface = canvas.GetSurface();
1130 if (surface == nullptr) {
1131 GE_LOGW("GEContourDiagonalFlowLightShader::CreateDrawImg surface is invalid");
1132 return nullptr;
1133 }
1134 auto offscreenSurface = surface->MakeSurface(rect.GetWidth(), rect.GetHeight());
1135 if (offscreenSurface == nullptr) {
1136 GE_LOGW("GEContourDiagonalFlowLightShader::CreateDrawImg offscreenSurface is invalid");
1137 return nullptr;
1138 }
1139 auto offscreenCanvas = offscreenSurface->GetCanvas();
1140 if (offscreenCanvas == nullptr) {
1141 GE_LOGW("GEContourDiagonalFlowLightShader::CreateDrawImg offscreenCanvas is invalid");
1142 return nullptr;
1143 }
1144 offscreenCanvas->AttachBrush(brush);
1145 offscreenCanvas->DrawRect(rect);
1146 offscreenCanvas->DetachBrush();
1147 return offscreenSurface->GetImageSnapshot();
1148 }
1149
BlendImg(Drawing::Canvas & canvas,std::shared_ptr<Drawing::Image> precalculationImg,std::shared_ptr<Drawing::Image> img1,std::shared_ptr<Drawing::Image> img2)1150 std::shared_ptr<Drawing::Image> GEContourDiagonalFlowLightShader::BlendImg(Drawing::Canvas& canvas,
1151 std::shared_ptr<Drawing::Image> precalculationImg,
1152 std::shared_ptr<Drawing::Image> img1, std::shared_ptr<Drawing::Image> img2)
1153 {
1154 if (precalculationImg == nullptr || img1 == nullptr || img2 == nullptr) {
1155 return img1 == nullptr ? img2 : img1;
1156 }
1157 thread_local std::shared_ptr<Drawing::RuntimeEffect> blendShaderEffect_ = nullptr;
1158 if (blendShaderEffect_ == nullptr) {
1159 blendShaderEffect_ = Drawing::RuntimeEffect::CreateForShader(BLEND_IMG_PROG);
1160 }
1161 if (blendShaderEffect_ == nullptr) {
1162 GE_LOGE("GEContourDiagonalFlowLightShader contourDiagonalFlowLightShaderEffect_ is nullptr.");
1163 return nullptr;
1164 }
1165 auto blendBuilder = std::make_shared<Drawing::RuntimeShaderBuilder>(blendShaderEffect_);
1166 Drawing::Matrix matrix;
1167 if (cacheAnyPtr_ == nullptr) {
1168 GE_LOGW("GEContourDiagonalFlowLightShader BlendImg cache is nullptr.");
1169 return nullptr;
1170 }
1171 auto precalculationImage = std::any_cast<CacheDataType>(*cacheAnyPtr_).precalculationImg;
1172 if (precalculationImage == nullptr) {
1173 cacheAnyPtr_ = nullptr;
1174 GE_LOGW("GEContourDiagonalFlowLightShader BlendImg cache img is nullptr.");
1175 return nullptr;
1176 }
1177 auto precalculationImgShader = Drawing::ShaderEffect::CreateImageShader(*precalculationImage,
1178 Drawing::TileMode::CLAMP, Drawing::TileMode::CLAMP,
1179 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
1180 auto image1Shader = Drawing::ShaderEffect::CreateImageShader(*img1, Drawing::TileMode::CLAMP,
1181 Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
1182 auto image2Shader = Drawing::ShaderEffect::CreateImageShader(*img2, Drawing::TileMode::CLAMP,
1183 Drawing::TileMode::CLAMP, Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), matrix);
1184 blendBuilder->SetChild("precalculationImage", precalculationImgShader);
1185 blendBuilder->SetChild("image1", image1Shader);
1186 blendBuilder->SetChild("image2", image2Shader);
1187 blendBuilder->SetUniform("weight1", contourDiagonalFlowLightParams_.weight1);
1188 blendBuilder->SetUniform("weight2", contourDiagonalFlowLightParams_.weight2);
1189 blendBuilder->SetUniform("blurRadius", contourDiagonalFlowLightParams_.radius_);
1190 auto imageInfo = img1->GetImageInfo();
1191 return blendBuilder->MakeImage(canvas.GetGPUContext().get(), nullptr, imageInfo, false);
1192 }
1193
DrawShader(Drawing::Canvas & canvas,const Drawing::Rect & rect)1194 void GEContourDiagonalFlowLightShader::DrawShader(Drawing::Canvas& canvas, const Drawing::Rect& rect)
1195 {
1196 Preprocess(canvas, rect); // to calculate your cache data
1197 if (cacheAnyPtr_ == nullptr) {
1198 GE_LOGW("GEContourDiagonalFlowLightShader DrawShader cache is nullptr.");
1199 return;
1200 }
1201 auto precalculationImage = std::any_cast<CacheDataType>(*cacheAnyPtr_).precalculationImg;
1202 if (precalculationImage == nullptr) {
1203 GE_LOGW("GEContourDiagonalFlowLightShader DrawShader cache img is nullptr.");
1204 return;
1205 }
1206 auto lightImg = DrawRuntimeShader(canvas, rect);
1207 auto blurImg = blurShader_->ProcessImage(canvas, lightImg, rect, rect);
1208 auto resImg = BlendImg(canvas, precalculationImage, lightImg, blurImg);
1209 if (resImg) {
1210 Drawing::Brush brush;
1211 canvas.AttachBrush(brush);
1212 canvas.DrawImageRect(*resImg, rect, rect, Drawing::SamplingOptions());
1213 canvas.DetachBrush();
1214 } else {
1215 GE_LOGW("GEContourDiagonalFlowLightShader DrawShader blendimg is nullptr.");
1216 return;
1217 }
1218 }
1219
1220 } // namespace Rosen
1221 } // namespace OHOS