1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "platform/common/rs_log.h"
17 #include "render/rs_particles_drawable.h"
18 #include "render/rs_pixel_map_util.h"
19
20 namespace OHOS {
21 namespace Rosen {
22 constexpr float DEGREE_TO_RADIAN = M_PI / 180;
23 constexpr float DOUBLE = 2.f;
24 constexpr float DEFAULT_RADIUS = 100;
25 constexpr int MAX_ATLAS_COUNT = 2000;
RSParticlesDrawable(const std::vector<std::shared_ptr<RSRenderParticle>> & particles,std::vector<std::shared_ptr<RSImage>> & imageVector,size_t imageCount)26 RSParticlesDrawable::RSParticlesDrawable(const std::vector<std::shared_ptr<RSRenderParticle>>& particles,
27 std::vector<std::shared_ptr<RSImage>>& imageVector, size_t imageCount)
28 : particles_(particles), imageVector_(imageVector), imageCount_(imageCount)
29 {
30 count_.resize(imageCount_);
31 imageRsxform_.resize(imageCount_);
32 imageTex_.resize(imageCount_);
33 imageColors_.resize(imageCount_);
34 }
35
MakeCircleImage(int radius)36 std::shared_ptr<Drawing::Image> RSParticlesDrawable::MakeCircleImage(int radius)
37 {
38 Drawing::Bitmap bitmap;
39 Drawing::BitmapFormat format { Drawing::ColorType::COLORTYPE_RGBA_8888, Drawing::AlphaType::ALPHATYPE_OPAQUE };
40 bitmap.Build(radius * DOUBLE, radius * DOUBLE, format);
41 bitmap.ClearWithColor(Drawing::Color::COLOR_TRANSPARENT);
42 auto recordingCanvas = Drawing::Canvas();
43 recordingCanvas.Bind(bitmap);
44 Drawing::Brush brush;
45 brush.SetBlendMode(Drawing::BlendMode::CLEAR);
46 Drawing::Rect r = { 0, 0, radius * DOUBLE, radius * DOUBLE };
47 recordingCanvas.AttachBrush(brush);
48 recordingCanvas.DrawRect(r);
49 recordingCanvas.DetachBrush();
50 brush.SetColor(Drawing::Color::COLOR_BLACK);
51 brush.SetBlendMode(Drawing::BlendMode::SRC_OVER);
52 brush.SetAntiAlias(true);
53 recordingCanvas.AttachBrush(brush);
54 recordingCanvas.DrawOval(r);
55 recordingCanvas.DetachBrush();
56 return bitmap.MakeImage();
57 }
58
59 /**
60 * A compressed form of a rotation+scale matrix.
61 *
62 * [ cos -sin tx ]
63 * [ sin cos ty ]
64 * [ 0 0 1 ]
65 */
MakeRSXform(Vector2f center,Vector2f position,float spin,float scale)66 Drawing::RSXform RSParticlesDrawable::MakeRSXform(Vector2f center, Vector2f position, float spin, float scale)
67 {
68 float cos = std::cos(spin * DEGREE_TO_RADIAN) * scale;
69 float sin = std::sin(spin * DEGREE_TO_RADIAN) * scale;
70 float tx = position.x_ - cos * center.x_ + sin * center.y_;
71 float ty = position.y_ - sin * center.x_ - cos * center.y_;
72 return Drawing::RSXform::Make(cos, sin, tx, ty);
73 }
74
CaculatePointAtlsArry(const std::shared_ptr<RSRenderParticle> & particle,Vector2f position,float opacity,float scale)75 void RSParticlesDrawable::CaculatePointAtlsArry(
76 const std::shared_ptr<RSRenderParticle>& particle, Vector2f position, float opacity, float scale)
77 {
78 if (particle == nullptr) {
79 return;
80 }
81 if (circleImage_ == nullptr) {
82 circleImage_ = MakeCircleImage(DEFAULT_RADIUS);
83 }
84 auto radius = particle->GetRadius();
85 Color color = particle->GetColor();
86 auto alpha = color.GetAlpha();
87 color.SetAlpha(alpha * opacity);
88 pointRsxform_.push_back(
89 MakeRSXform(Vector2f(DEFAULT_RADIUS, DEFAULT_RADIUS), position, 0.f, radius * scale / DEFAULT_RADIUS));
90 pointTex_.push_back(Drawing::Rect(0, 0, DEFAULT_RADIUS * DOUBLE, DEFAULT_RADIUS * DOUBLE));
91 pointColors_.push_back(Drawing::Color(color.AsArgbInt()).CastToColorQuad());
92 pointCount_++;
93 }
94
CaculateImageAtlsArry(Drawing::Canvas & canvas,const std::shared_ptr<RSRenderParticle> & particle,Vector2f position,float opacity,float scale)95 void RSParticlesDrawable::CaculateImageAtlsArry(Drawing::Canvas& canvas,
96 const std::shared_ptr<RSRenderParticle>& particle, Vector2f position, float opacity, float scale)
97 {
98 if (particle == nullptr) {
99 return;
100 }
101 auto image = particle->GetImage();
102 if (image == nullptr) {
103 return;
104 }
105 auto pixelmap = image->GetPixelMap();
106 if (pixelmap == nullptr) {
107 return;
108 }
109 auto imageIndex = particle->GetImageIndex();
110 if (imageIndex >= imageCount_) {
111 return;
112 }
113 auto imageSize = particle->GetImageSize();
114 auto spin = particle->GetSpin();
115 float left = position.x_;
116 float top = position.y_;
117 float width = imageSize.x_;
118 float height = imageSize.y_;
119 RectF destRect(left, top, width, height);
120 auto imageFit = image->GetImageFit();
121 if (imageFit != ImageFit::FILL) {
122 image->SetFrameRect(destRect);
123 image->ApplyImageFit();
124 Color color = particle->GetColor();
125 auto alpha = color.GetAlpha();
126 color.SetAlpha(alpha * opacity);
127 imageRsxform_[imageIndex].push_back(
128 MakeRSXform(Vector2f(pixelmap->GetWidth() / DOUBLE, pixelmap->GetHeight() / DOUBLE), position, spin,
129 image->GetDstRect().GetWidth() / pixelmap->GetWidth() * scale));
130 imageTex_[imageIndex].push_back(Drawing::Rect(0, 0, pixelmap->GetWidth(), pixelmap->GetHeight()));
131 imageColors_[imageIndex].push_back(Drawing::Color(color.AsArgbInt()).CastToColorQuad());
132 count_[imageIndex]++;
133 } else {
134 DrawImageFill(canvas, particle, position, opacity, scale);
135 }
136 }
137
DrawImageFill(Drawing::Canvas & canvas,const std::shared_ptr<RSRenderParticle> & particle,Vector2f position,float opacity,float scale)138 void RSParticlesDrawable::DrawImageFill(Drawing::Canvas& canvas, const std::shared_ptr<RSRenderParticle>& particle,
139 Vector2f position, float opacity, float scale)
140 {
141 if (particle == nullptr) {
142 return;
143 }
144 auto image = particle->GetImage();
145 if (image == nullptr) {
146 return;
147 }
148 auto imageSize = particle->GetImageSize();
149 auto spin = particle->GetSpin();
150 float left = position.x_;
151 float top = position.y_;
152 float right = position.x_ + imageSize.x_ * scale;
153 float bottom = position.y_ + imageSize.y_ * scale;
154 canvas.Save();
155 canvas.Translate(position.x_, position.y_);
156 canvas.Rotate(spin, imageSize.x_ * scale / DOUBLE, imageSize.y_ * scale / DOUBLE);
157 image->SetScale(scale);
158 image->SetImageRepeat(0);
159 Drawing::Rect rect { left, top, right, bottom };
160 Drawing::Brush brush;
161 brush.SetAntiAlias(true);
162 brush.SetAlphaF(opacity);
163 canvas.AttachBrush(brush);
164 image->CanvasDrawImage(canvas, rect, Drawing::SamplingOptions(), false);
165 canvas.DetachBrush();
166 canvas.Restore();
167 }
168
Draw(Drawing::Canvas & canvas,std::shared_ptr<RectF> bounds)169 void RSParticlesDrawable::Draw(Drawing::Canvas& canvas, std::shared_ptr<RectF> bounds)
170 {
171 if (particles_.empty()) {
172 ROSEN_LOGE("RSParticlesDrawable::Draw particles_ is empty");
173 return;
174 }
175 for (const auto& particle : particles_) {
176 if (particle != nullptr && particle->IsAlive()) {
177 auto position = particle->GetPosition();
178 float opacity = particle->GetOpacity();
179 float scale = particle->GetScale();
180 if (bounds == nullptr || !(bounds->Intersect(position.x_, position.y_)) || opacity <= 0.f || scale <= 0.f) {
181 continue;
182 }
183 auto particleType = particle->GetParticleType();
184 auto clipBounds = Drawing::Rect(
185 bounds->left_, bounds->top_, bounds->left_ + bounds->width_, bounds->top_ + bounds->height_);
186 canvas.ClipRect(clipBounds, Drawing::ClipOp::INTERSECT, true);
187 if (particleType == ParticleType::POINTS) {
188 CaculatePointAtlsArry(particle, position, opacity, scale);
189 } else {
190 CaculateImageAtlsArry(canvas, particle, position, opacity, scale);
191 }
192 }
193 }
194 DrawParticles(canvas);
195 }
196
DrawParticles(Drawing::Canvas & canvas)197 void RSParticlesDrawable::DrawParticles(Drawing::Canvas& canvas)
198 {
199 if (circleImage_ != nullptr) {
200 DrawCircle(canvas);
201 }
202 if (imageCount_ > 0) {
203 DrawImages(canvas);
204 }
205 }
206
DrawCircle(Drawing::Canvas & canvas)207 void RSParticlesDrawable::DrawCircle(Drawing::Canvas& canvas)
208 {
209 Drawing::Brush brush;
210 brush.SetAntiAlias(true);
211 Drawing::RSXform* rsxform = pointRsxform_.data();
212 Drawing::Rect* tex = pointTex_.data();
213 Drawing::ColorQuad* colors = pointColors_.data();
214 canvas.AttachBrush(brush);
215 while (pointCount_ > MAX_ATLAS_COUNT) {
216 canvas.DrawAtlas(circleImage_.get(), rsxform, tex, colors, MAX_ATLAS_COUNT, Drawing::BlendMode::DST_IN,
217 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), nullptr);
218 pointCount_ -= MAX_ATLAS_COUNT;
219 rsxform += MAX_ATLAS_COUNT;
220 tex += MAX_ATLAS_COUNT;
221 colors += MAX_ATLAS_COUNT;
222 }
223 canvas.DrawAtlas(circleImage_.get(), rsxform, tex, colors, pointCount_, Drawing::BlendMode::DST_IN,
224 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), nullptr);
225 canvas.DetachBrush();
226 }
227
DrawImages(Drawing::Canvas & canvas)228 void RSParticlesDrawable::DrawImages(Drawing::Canvas& canvas)
229 {
230 while (imageCount_--) {
231 if (imageVector_[imageCount_] != nullptr) {
232 auto pixelmap = imageVector_[imageCount_]->GetPixelMap();
233 if (!pixelmap) {
234 ROSEN_LOGE("RSParticlesDrawable::Draw !pixel");
235 return;
236 }
237 auto image = RSPixelMapUtil::ExtractDrawingImage(pixelmap);
238 if (!image) {
239 ROSEN_LOGE("RSParticlesDrawable::Draw !image");
240 return;
241 }
242 Drawing::Brush brush;
243 brush.SetAntiAlias(true);
244 int count = count_[imageCount_];
245 Drawing::RSXform* rsxform = imageRsxform_[imageCount_].data();
246 Drawing::Rect* tex = imageTex_[imageCount_].data();
247 Drawing::ColorQuad* colors = imageColors_[imageCount_].data();
248 canvas.AttachBrush(brush);
249 while (count > MAX_ATLAS_COUNT) {
250 canvas.DrawAtlas(image.get(), rsxform, tex, colors, MAX_ATLAS_COUNT, Drawing::BlendMode::SRC_IN,
251 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), nullptr);
252 count -= MAX_ATLAS_COUNT;
253 rsxform += MAX_ATLAS_COUNT;
254 tex += MAX_ATLAS_COUNT;
255 colors += MAX_ATLAS_COUNT;
256 }
257 canvas.DrawAtlas(image.get(), rsxform, tex, colors, count, Drawing::BlendMode::SRC_IN,
258 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), nullptr);
259 canvas.DetachBrush();
260 }
261 }
262 }
263
264 } // namespace Rosen
265 } // namespace OHOS
266