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