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