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