1 /*
2  * Copyright (c) 2021-2023 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 "skia_shader_effect.h"
17 
18 #include <vector>
19 
20 #include "include/core/SkMatrix.h"
21 #include "include/core/SkSamplingOptions.h"
22 #include "include/core/SkTileMode.h"
23 #include "include/effects/SkGradientShader.h"
24 #include "include/effects/SkRuntimeEffect.h"
25 #include "src/core/SkReadBuffer.h"
26 #include "src/core/SkWriteBuffer.h"
27 #include "src/shaders/SkShaderBase.h"
28 
29 #include "skia_helper.h"
30 #include "skia_image.h"
31 #include "skia_matrix.h"
32 #include "skia_picture.h"
33 
34 #include "effect/shader_effect.h"
35 #include "image/image.h"
36 #include "image/picture.h"
37 #include "utils/matrix.h"
38 #include "utils/data.h"
39 #include "utils/log.h"
40 
41 namespace OHOS {
42 namespace Rosen {
43 namespace Drawing {
SkiaShaderEffect()44 SkiaShaderEffect::SkiaShaderEffect() noexcept : shader_(nullptr) {}
45 
InitWithColor(ColorQuad color)46 void SkiaShaderEffect::InitWithColor(ColorQuad color)
47 {
48     shader_ = SkShaders::Color(color);
49 }
50 
InitWithColorSpace(const Color4f & color,std::shared_ptr<ColorSpace> colorSpace)51 void SkiaShaderEffect::InitWithColorSpace(const Color4f& color, std::shared_ptr<ColorSpace> colorSpace)
52 {
53     const SkColor4f& skC4f = { .fR = color.redF_, .fG = color.greenF_, .fB = color.blueF_, .fA = color.alphaF_ };
54     shader_ = SkShaders::Color(skC4f, colorSpace->GetSkColorSpace());
55 }
56 
InitWithBlend(const ShaderEffect & s1,const ShaderEffect & s2,BlendMode mode)57 void SkiaShaderEffect::InitWithBlend(const ShaderEffect& s1, const ShaderEffect& s2, BlendMode mode)
58 {
59     auto dst = s1.GetImpl<SkiaShaderEffect>();
60     auto src = s2.GetImpl<SkiaShaderEffect>();
61     if (dst != nullptr && src != nullptr) {
62         shader_ = SkShaders::Blend(static_cast<SkBlendMode>(mode), dst->GetShader(), src->GetShader());
63     }
64 }
65 
InitWithImage(const Image & image,TileMode tileX,TileMode tileY,const SamplingOptions & sampling,const Matrix & matrix)66 void SkiaShaderEffect::InitWithImage(
67     const Image& image, TileMode tileX, TileMode tileY, const SamplingOptions& sampling, const Matrix& matrix)
68 {
69     SkTileMode modeX = static_cast<SkTileMode>(tileX);
70     SkTileMode modeY = static_cast<SkTileMode>(tileY);
71 
72     auto m = matrix.GetImpl<SkiaMatrix>();
73     auto i = image.GetImpl<SkiaImage>();
74     SkMatrix skiaMatrix;
75     sk_sp<SkImage> skiaImage;
76     if (m != nullptr && i != nullptr) {
77         skiaMatrix = m->ExportSkiaMatrix();
78         skiaImage = i->GetImage();
79         if (skiaImage != nullptr) {
80             SkSamplingOptions samplingOptions;
81             if (sampling.GetUseCubic()) {
82                 samplingOptions = SkSamplingOptions({ sampling.GetCubicCoffB(), sampling.GetCubicCoffC() });
83             } else {
84                 samplingOptions = SkSamplingOptions(static_cast<SkFilterMode>(sampling.GetFilterMode()),
85                     static_cast<SkMipmapMode>(sampling.GetMipmapMode()));
86             }
87             shader_ = skiaImage->makeShader(modeX, modeY, samplingOptions, &skiaMatrix);
88         }
89     }
90 }
91 
InitWithPicture(const Picture & picture,TileMode tileX,TileMode tileY,FilterMode mode,const Matrix & matrix,const Rect & rect)92 void SkiaShaderEffect::InitWithPicture(
93     const Picture& picture, TileMode tileX, TileMode tileY, FilterMode mode, const Matrix& matrix, const Rect& rect)
94 {
95     SkTileMode modeX = static_cast<SkTileMode>(tileX);
96     SkTileMode modeY = static_cast<SkTileMode>(tileY);
97     SkRect r = SkRect::MakeLTRB(rect.GetLeft(), rect.GetTop(), rect.GetRight(), rect.GetBottom());
98 
99     auto p = picture.GetImpl<SkiaPicture>();
100     auto m = matrix.GetImpl<SkiaMatrix>();
101     sk_sp<SkPicture> skiaPicture;
102     SkMatrix skiaMatrix;
103     if (p != nullptr && m != nullptr) {
104         skiaPicture = p->GetPicture();
105         skiaMatrix = m->ExportSkiaMatrix();
106         if (skiaPicture != nullptr) {
107             SkFilterMode skFilterMode = static_cast<SkFilterMode>(mode);
108             shader_ = skiaPicture->makeShader(modeX, modeY, skFilterMode, &skiaMatrix, &r);
109         }
110     }
111 }
112 
InitWithLinearGradient(const Point & startPt,const Point & endPt,const std::vector<ColorQuad> & colors,const std::vector<scalar> & pos,TileMode mode,const Matrix * matrix)113 void SkiaShaderEffect::InitWithLinearGradient(const Point& startPt, const Point& endPt,
114     const std::vector<ColorQuad>& colors, const std::vector<scalar>& pos, TileMode mode, const Matrix *matrix)
115 {
116     SkPoint pts[2];
117     pts[0].set(startPt.GetX(), startPt.GetY());
118     pts[1].set(endPt.GetX(), endPt.GetY());
119 
120     if (colors.empty()) {
121         return;
122     }
123     size_t colorsCount = colors.size();
124 
125     std::vector<SkColor> c;
126     std::vector<SkScalar> p;
127     for (size_t i = 0; i < colorsCount; ++i) {
128         c.emplace_back(colors[i]);
129     }
130     for (size_t i = 0; i < pos.size(); ++i) {
131         p.emplace_back(pos[i]);
132     }
133     const SkMatrix *skMatrix = nullptr;
134     if (matrix != nullptr) {
135         skMatrix = &matrix->GetImpl<SkiaMatrix>()->ExportSkiaMatrix();
136     }
137     shader_ = SkGradientShader::MakeLinear(pts, &c[0], pos.empty() ? nullptr : &p[0],
138         colorsCount, static_cast<SkTileMode>(mode), 0, skMatrix);
139 }
140 
InitWithRadialGradient(const Point & centerPt,scalar radius,const std::vector<ColorQuad> & colors,const std::vector<scalar> & pos,TileMode mode,const Matrix * matrix)141 void SkiaShaderEffect::InitWithRadialGradient(const Point& centerPt, scalar radius,
142     const std::vector<ColorQuad>& colors, const std::vector<scalar>& pos, TileMode mode, const Matrix *matrix)
143 {
144     SkPoint center;
145     center.set(centerPt.GetX(), centerPt.GetY());
146 
147     if (colors.empty()) {
148         return;
149     }
150     size_t colorsCount = colors.size();
151 
152     std::vector<SkColor> c;
153     std::vector<SkScalar> p;
154     for (size_t i = 0; i < colorsCount; ++i) {
155         c.emplace_back(colors[i]);
156     }
157     for (size_t i = 0; i < pos.size(); ++i) {
158         p.emplace_back(pos[i]);
159     }
160     const SkMatrix *skMatrix = nullptr;
161     if (matrix != nullptr) {
162         skMatrix = &matrix->GetImpl<SkiaMatrix>()->ExportSkiaMatrix();
163     }
164     shader_ = SkGradientShader::MakeRadial(center, radius, &c[0],
165         pos.empty() ? nullptr : &p[0], colorsCount, static_cast<SkTileMode>(mode), 0, skMatrix);
166 }
167 
InitWithTwoPointConical(const Point & startPt,scalar startRadius,const Point & endPt,scalar endRadius,const std::vector<ColorQuad> & colors,const std::vector<scalar> & pos,TileMode mode,const Matrix * matrix)168 void SkiaShaderEffect::InitWithTwoPointConical(const Point& startPt, scalar startRadius, const Point& endPt,
169     scalar endRadius, const std::vector<ColorQuad>& colors, const std::vector<scalar>& pos, TileMode mode,
170     const Matrix *matrix)
171 {
172     SkPoint start;
173     SkPoint end;
174     start.set(startPt.GetX(), startPt.GetY());
175     end.set(endPt.GetX(), endPt.GetY());
176 
177     if (colors.empty()) {
178         return;
179     }
180     size_t colorsCount = colors.size();
181 
182     std::vector<SkColor> c;
183     std::vector<SkScalar> p;
184     for (size_t i = 0; i < colorsCount; ++i) {
185         c.emplace_back(colors[i]);
186     }
187     for (size_t i = 0; i < pos.size(); ++i) {
188         p.emplace_back(pos[i]);
189     }
190     const SkMatrix *skMatrix = nullptr;
191     if (matrix != nullptr) {
192         skMatrix = &matrix->GetImpl<SkiaMatrix>()->ExportSkiaMatrix();
193     }
194 
195     shader_ = SkGradientShader::MakeTwoPointConical(start, startRadius, end, endRadius,
196         &c[0], pos.empty() ? nullptr : &p[0], colorsCount, static_cast<SkTileMode>(mode), 0, skMatrix);
197 }
198 
InitWithSweepGradient(const Point & centerPt,const std::vector<ColorQuad> & colors,const std::vector<scalar> & pos,TileMode mode,scalar startAngle,scalar endAngle,const Matrix * matrix)199 void SkiaShaderEffect::InitWithSweepGradient(const Point& centerPt, const std::vector<ColorQuad>& colors,
200     const std::vector<scalar>& pos, TileMode mode, scalar startAngle, scalar endAngle, const Matrix *matrix)
201 {
202     if (colors.empty()) {
203         return;
204     }
205     size_t colorsCount = colors.size();
206 
207     std::vector<SkColor> c;
208     std::vector<SkScalar> p;
209     for (size_t i = 0; i < colorsCount; ++i) {
210         c.emplace_back(colors[i]);
211     }
212     for (size_t i = 0; i < pos.size(); ++i) {
213         p.emplace_back(pos[i]);
214     }
215     const SkMatrix *skMatrix = nullptr;
216     if (matrix != nullptr) {
217         skMatrix = &matrix->GetImpl<SkiaMatrix>()->ExportSkiaMatrix();
218     }
219     shader_ = SkGradientShader::MakeSweep(centerPt.GetX(), centerPt.GetY(), &c[0],
220         pos.empty() ? nullptr : &p[0], colorsCount,
221         static_cast<SkTileMode>(mode), startAngle, endAngle, 0, skMatrix);
222 }
223 
InitWithLightUp(const float & lightUpDeg,const ShaderEffect & imageShader)224 void SkiaShaderEffect::InitWithLightUp(const float& lightUpDeg, const ShaderEffect& imageShader)
225 {
226     auto imageShaderImpl_ = imageShader.GetImpl<SkiaShaderEffect>();
227     if (imageShaderImpl_ != nullptr) {
228         static constexpr char prog[] = R"(
229             uniform half lightUpDeg;
230             uniform shader imageShader;
231             vec3 rgb2hsv(in vec3 c)
232             {
233                 vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
234                 vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
235                 vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
236                 float d = q.x - min(q.w, q.y);
237                 float e = 1.0e-10;
238                 return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
239             }
240             vec3 hsv2rgb(in vec3 c)
241             {
242                 vec3 rgb = clamp(abs(mod(c.x * 6.0 + vec3(0.0, 4.0, 2.0), 6.0) - 3.0) - 1.0, 0.0, 1.0);
243                 return c.z * mix(vec3(1.0), rgb, c.y);
244             }
245             half4 main(float2 coord)
246             {
247                 vec3 hsv = rgb2hsv(imageShader.eval(coord).rgb);
248                 float satUpper = clamp(hsv.y * 1.2, 0.0, 1.0);
249                 hsv.y = mix(satUpper, hsv.y, lightUpDeg);
250                 hsv.z += lightUpDeg - 1.0;
251                 return vec4(hsv2rgb(hsv), imageShader.eval(coord).a);
252             }
253         )";
254         auto [effect, err] = SkRuntimeEffect::MakeForShader(SkString(prog));
255         sk_sp<SkShader> children[] = {imageShaderImpl_->GetShader()};
256         size_t childCount = 1;
257         shader_ = effect->makeShader(SkData::MakeWithCopy(
258             &lightUpDeg, sizeof(lightUpDeg)), children, childCount, nullptr, false);
259     } else {
260         LOGE("SkiaShaderEffect::InitWithLightUp: imageShader is nullptr");
261     }
262 }
263 
GetShader() const264 sk_sp<SkShader> SkiaShaderEffect::GetShader() const
265 {
266     return shader_;
267 }
268 
SetSkShader(const sk_sp<SkShader> & skShader)269 void SkiaShaderEffect::SetSkShader(const sk_sp<SkShader>& skShader)
270 {
271     shader_ = skShader;
272 }
273 
Serialize() const274 std::shared_ptr<Data> SkiaShaderEffect::Serialize() const
275 {
276     if (shader_ == nullptr) {
277         LOGD("SkiaShaderEffect::Serialize, shader_ is nullptr!");
278         return nullptr;
279     }
280 
281     SkBinaryWriteBuffer writer;
282     writer.writeFlattenable(shader_.get());
283     size_t length = writer.bytesWritten();
284     std::shared_ptr<Data> data = std::make_shared<Data>();
285     data->BuildUninitialized(length);
286     writer.writeToMemory(data->WritableData());
287     return data;
288 }
289 
Deserialize(std::shared_ptr<Data> data)290 bool SkiaShaderEffect::Deserialize(std::shared_ptr<Data> data)
291 {
292     if (data == nullptr) {
293         LOGD("SkiaShaderEffect::Deserialize, data is invalid!");
294         return false;
295     }
296     SkReadBuffer reader(data->GetData(), data->GetSize());
297     shader_ = reader.readShader();
298     return true;
299 }
300 
301 } // namespace Drawing
302 } // namespace Rosen
303 } // namespace OHOS