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 auto drawImage = image->GetImage();
107 if (pixelmap == nullptr && drawImage == nullptr) {
108 return;
109 }
110 auto imageIndex = particle->GetImageIndex();
111 if (imageIndex >= imageCount_) {
112 return;
113 }
114 auto imageSize = particle->GetImageSize();
115 auto spin = particle->GetSpin();
116 float left = position.x_;
117 float top = position.y_;
118 float width = imageSize.x_;
119 float height = imageSize.y_;
120 RectF destRect(left, top, width, height);
121 auto imageFit = image->GetImageFit();
122 if (imageFit != ImageFit::FILL) {
123 image->SetFrameRect(destRect);
124 image->ApplyImageFit();
125 Color color = particle->GetColor();
126 auto alpha = color.GetAlpha();
127 color.SetAlpha(alpha * opacity);
128 auto width = pixelmap == nullptr ? drawImage->GetWidth() : pixelmap->GetWidth();
129 auto height = pixelmap == nullptr ? drawImage->GetHeight() : pixelmap->GetHeight();
130 imageRsxform_[imageIndex].push_back(
131 MakeRSXform(Vector2f(width / DOUBLE, height / DOUBLE), position, spin,
132 image->GetDstRect().GetWidth() / width * scale));
133 imageTex_[imageIndex].push_back(Drawing::Rect(0, 0, width, height));
134 imageColors_[imageIndex].push_back(Drawing::Color(color.AsArgbInt()).CastToColorQuad());
135 count_[imageIndex]++;
136 } else {
137 DrawImageFill(canvas, particle, position, opacity, scale);
138 }
139 }
140
DrawImageFill(Drawing::Canvas & canvas,const std::shared_ptr<RSRenderParticle> & particle,Vector2f position,float opacity,float scale)141 void RSParticlesDrawable::DrawImageFill(Drawing::Canvas& canvas, const std::shared_ptr<RSRenderParticle>& particle,
142 Vector2f position, float opacity, float scale)
143 {
144 if (particle == nullptr) {
145 return;
146 }
147 auto image = particle->GetImage();
148 if (image == nullptr) {
149 return;
150 }
151 auto imageSize = particle->GetImageSize();
152 auto spin = particle->GetSpin();
153 float left = position.x_;
154 float top = position.y_;
155 float right = position.x_ + imageSize.x_ * scale;
156 float bottom = position.y_ + imageSize.y_ * scale;
157 canvas.Save();
158 canvas.Translate(position.x_, position.y_);
159 canvas.Rotate(spin, imageSize.x_ * scale / DOUBLE, imageSize.y_ * scale / DOUBLE);
160 image->SetScale(scale);
161 image->SetImageRepeat(0);
162 Drawing::Rect rect { left, top, right, bottom };
163 Drawing::Brush brush;
164 brush.SetAntiAlias(true);
165 brush.SetAlphaF(opacity);
166 canvas.AttachBrush(brush);
167 image->CanvasDrawImage(canvas, rect, Drawing::SamplingOptions(), false);
168 canvas.DetachBrush();
169 canvas.Restore();
170 }
171
Draw(Drawing::Canvas & canvas,std::shared_ptr<RectF> bounds)172 void RSParticlesDrawable::Draw(Drawing::Canvas& canvas, std::shared_ptr<RectF> bounds)
173 {
174 if (particles_.empty()) {
175 ROSEN_LOGE("RSParticlesDrawable::Draw particles_ is empty");
176 return;
177 }
178 for (const auto& particle : particles_) {
179 if (particle != nullptr && particle->IsAlive()) {
180 auto position = particle->GetPosition();
181 float opacity = particle->GetOpacity();
182 float scale = particle->GetScale();
183 if (bounds == nullptr || !(bounds->Intersect(position.x_, position.y_)) || opacity <= 0.f || scale <= 0.f) {
184 continue;
185 }
186 auto particleType = particle->GetParticleType();
187 auto clipBounds = Drawing::Rect(
188 bounds->left_, bounds->top_, bounds->left_ + bounds->width_, bounds->top_ + bounds->height_);
189 canvas.ClipRect(clipBounds, Drawing::ClipOp::INTERSECT, true);
190 if (particleType == ParticleType::POINTS) {
191 CaculatePointAtlsArry(particle, position, opacity, scale);
192 } else {
193 CaculateImageAtlsArry(canvas, particle, position, opacity, scale);
194 }
195 }
196 }
197 DrawParticles(canvas);
198 }
199
DrawParticles(Drawing::Canvas & canvas)200 void RSParticlesDrawable::DrawParticles(Drawing::Canvas& canvas)
201 {
202 if (circleImage_ != nullptr) {
203 DrawCircle(canvas);
204 }
205 if (imageCount_ > 0) {
206 DrawImages(canvas);
207 }
208 }
209
DrawCircle(Drawing::Canvas & canvas)210 void RSParticlesDrawable::DrawCircle(Drawing::Canvas& canvas)
211 {
212 Drawing::Brush brush;
213 brush.SetAntiAlias(true);
214 Drawing::RSXform* rsxform = pointRsxform_.data();
215 Drawing::Rect* tex = pointTex_.data();
216 Drawing::ColorQuad* colors = pointColors_.data();
217 canvas.AttachBrush(brush);
218 while (pointCount_ > MAX_ATLAS_COUNT) {
219 canvas.DrawAtlas(circleImage_.get(), rsxform, tex, colors, MAX_ATLAS_COUNT, Drawing::BlendMode::DST_IN,
220 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), nullptr);
221 pointCount_ -= MAX_ATLAS_COUNT;
222 rsxform += MAX_ATLAS_COUNT;
223 tex += MAX_ATLAS_COUNT;
224 colors += MAX_ATLAS_COUNT;
225 }
226 canvas.DrawAtlas(circleImage_.get(), rsxform, tex, colors, pointCount_, Drawing::BlendMode::DST_IN,
227 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), nullptr);
228 canvas.DetachBrush();
229 }
230
CheckImageNull(std::shared_ptr<Drawing::Image> & image,const std::shared_ptr<Drawing::Image> & drawImage)231 bool RSParticlesDrawable::CheckImageNull(std::shared_ptr<Drawing::Image>& image,
232 const std::shared_ptr<Drawing::Image>& drawImage)
233 {
234 if (image) {
235 return false;
236 }
237
238 if (drawImage) {
239 image = drawImage;
240 return false;
241 }
242
243 return true;
244 }
245
DrawImages(Drawing::Canvas & canvas)246 void RSParticlesDrawable::DrawImages(Drawing::Canvas& canvas)
247 {
248 while (imageCount_--) {
249 if (imageVector_[imageCount_] != nullptr) {
250 std::shared_ptr<Drawing::Image> image = nullptr;
251 auto pixelmap = imageVector_[imageCount_]->GetPixelMap();
252 if (pixelmap) {
253 image = RSPixelMapUtil::ExtractDrawingImage(pixelmap);
254 }
255
256 if (CheckImageNull(image, imageVector_[imageCount_]->GetImage())) {
257 ROSEN_LOGE("RSParticlesDrawable::Draw !pixel and !image_");
258 return;
259 }
260 Drawing::Brush brush;
261 brush.SetAntiAlias(true);
262 int count = count_[imageCount_];
263 Drawing::RSXform* rsxform = imageRsxform_[imageCount_].data();
264 Drawing::Rect* tex = imageTex_[imageCount_].data();
265 Drawing::ColorQuad* colors = imageColors_[imageCount_].data();
266 canvas.AttachBrush(brush);
267 while (count > MAX_ATLAS_COUNT) {
268 canvas.DrawAtlas(image.get(), rsxform, tex, colors, MAX_ATLAS_COUNT, Drawing::BlendMode::SRC_IN,
269 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), nullptr);
270 count -= MAX_ATLAS_COUNT;
271 rsxform += MAX_ATLAS_COUNT;
272 tex += MAX_ATLAS_COUNT;
273 colors += MAX_ATLAS_COUNT;
274 }
275 canvas.DrawAtlas(image.get(), rsxform, tex, colors, count, Drawing::BlendMode::SRC_IN,
276 Drawing::SamplingOptions(Drawing::FilterMode::LINEAR), nullptr);
277 canvas.DetachBrush();
278 }
279 }
280 }
281
282 } // namespace Rosen
283 } // namespace OHOS
284