1 /*
2 * Copyright (c) 2021 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 "core/image/image_object.h"
17
18 #include "base/thread/background_task_executor.h"
19 #include "core/common/container.h"
20 #include "core/common/container_scope.h"
21 #include "core/components/image/render_image.h"
22 #include "core/image/flutter_image_cache.h"
23
24 namespace OHOS::Ace {
25
GenerateCacheKey(const ImageSourceInfo & srcInfo,Size targetImageSize)26 std::string ImageObject::GenerateCacheKey(const ImageSourceInfo& srcInfo, Size targetImageSize)
27 {
28 return srcInfo.GetCacheKey() + std::to_string(static_cast<int32_t>(targetImageSize.Width())) +
29 std::to_string(static_cast<int32_t>(targetImageSize.Height()));
30 }
31
BuildImageObject(ImageSourceInfo source,const RefPtr<PipelineContext> context,const sk_sp<SkData> & skData,bool useSkiaSvg)32 RefPtr<ImageObject> ImageObject::BuildImageObject(
33 ImageSourceInfo source,
34 const RefPtr<PipelineContext> context,
35 const sk_sp<SkData>& skData,
36 bool useSkiaSvg)
37 {
38 // build svg image object.
39 if (source.IsSvg()) {
40 const auto svgStream = std::make_unique<SkMemoryStream>(skData);
41 if (!svgStream) {
42 return nullptr;
43 }
44 auto color = source.GetFillColor();
45 if (!useSkiaSvg) {
46 auto svgDom = SvgDom::CreateSvgDom(*svgStream, context, color);
47 return svgDom ? MakeRefPtr<SvgImageObject>(source, Size(), 1, svgDom) : nullptr;
48 } else {
49 uint64_t colorValue = 0;
50 if (color.has_value()) {
51 colorValue = color.value().GetValue();
52 // skia svg relies on the 32th bit to determine whether or not to use the color we set.
53 colorValue = colorValue | (static_cast<int64_t>(0b1) << 32);
54 }
55 auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, colorValue);
56 return skiaDom ? MakeRefPtr<SvgSkiaImageObject>(source, Size(), 1, skiaDom) : nullptr;
57 }
58 }
59 // build normal pixel image object.
60 auto codec = SkCodec::MakeFromData(skData);
61 int32_t totalFrames = 1;
62 Size imageSize;
63 if (codec) {
64 totalFrames = codec->getFrameCount();
65 switch (codec->getOrigin()) {
66 case SkEncodedOrigin::kLeftTop_SkEncodedOrigin:
67 case SkEncodedOrigin::kRightTop_SkEncodedOrigin:
68 case SkEncodedOrigin::kRightBottom_SkEncodedOrigin:
69 case SkEncodedOrigin::kLeftBottom_SkEncodedOrigin:
70 imageSize.SetSize(Size(codec->dimensions().fHeight, codec->dimensions().fWidth));
71 break;
72 default:
73 imageSize.SetSize(Size(codec->dimensions().fWidth, codec->dimensions().fHeight));
74 }
75 }
76 if (totalFrames == 1) {
77 return MakeRefPtr<StaticImageObject>(source, imageSize, totalFrames, skData);
78 } else {
79 return MakeRefPtr<AnimatedImageObject>(source, imageSize, totalFrames, skData);
80 }
81 }
82
MeasureForImage(RefPtr<RenderImage> image)83 Size ImageObject::MeasureForImage(RefPtr<RenderImage> image)
84 {
85 return image->MeasureForNormalImage();
86 }
87
PerformLayoutImageObject(RefPtr<RenderImage> image)88 void SvgImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
89 {
90 image->PerformLayoutSvgImage();
91 }
92
MeasureForImage(RefPtr<RenderImage> image)93 Size SvgImageObject::MeasureForImage(RefPtr<RenderImage> image)
94 {
95 return image->MeasureForSvgImage();
96 }
97
PerformLayoutImageObject(RefPtr<RenderImage> image)98 void SvgSkiaImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image) {}
99
MeasureForImage(RefPtr<RenderImage> image)100 Size SvgSkiaImageObject::MeasureForImage(RefPtr<RenderImage> image)
101 {
102 return image->MeasureForSvgImage();
103 }
104
UploadToGpuForRender(const WeakPtr<PipelineContext> context,RefPtr<FlutterRenderTaskHolder> & renderTaskHolder,UploadSuccessCallback successCallback,FailedCallback failedCallback,Size imageSize,bool forceResize,bool syncMode)105 void StaticImageObject::UploadToGpuForRender(
106 const WeakPtr<PipelineContext> context,
107 RefPtr<FlutterRenderTaskHolder>& renderTaskHolder,
108 UploadSuccessCallback successCallback,
109 FailedCallback failedCallback,
110 Size imageSize,
111 bool forceResize,
112 bool syncMode)
113 {
114 auto task = [context, renderTaskHolder, successCallback, failedCallback, imageSize, forceResize, skData = skData_,
115 imageSource = imageSource_, id = Container::CurrentId()]() mutable {
116 ContainerScope scope(id);
117 auto pipelineContext = context.Upgrade();
118 if (!pipelineContext) {
119 LOGE("pipline context has been released.");
120 return;
121 }
122 auto taskExecutor = pipelineContext->GetTaskExecutor();
123 if (!taskExecutor) {
124 LOGE("task executor is null.");
125 return;
126 }
127 fml::RefPtr<flutter::CanvasImage> cachedFlutterImage;
128 auto imageCache = pipelineContext->GetImageCache();
129 if (imageCache) {
130 auto cachedImage = imageCache->GetCacheImage(GenerateCacheKey(imageSource, imageSize));
131 LOGD("image cache valid");
132 if (cachedImage) {
133 LOGD("cached image found.");
134 cachedFlutterImage = cachedImage->imagePtr;
135 }
136 }
137 if (cachedFlutterImage) {
138 LOGD("get cached image success: %{public}s", GenerateCacheKey(imageSource, imageSize).c_str());
139 taskExecutor->PostTask([successCallback, imageSource,
140 cachedFlutterImage] { successCallback(imageSource, cachedFlutterImage); },
141 TaskExecutor::TaskType::UI);
142 return;
143 }
144
145 if (!skData) {
146 LOGD("reload sk data");
147 skData = ImageProvider::LoadImageRawData(imageSource, pipelineContext, imageSize);
148 if (!skData) {
149 LOGE("reload image data failed. imageSource: %{private}s", imageSource.ToString().c_str());
150 taskExecutor->PostTask(
151 [failedCallback, imageSource] { failedCallback(imageSource); }, TaskExecutor::TaskType::UI);
152 return;
153 }
154 }
155 auto rawImage = SkImage::MakeFromEncoded(skData);
156 if (!rawImage) {
157 LOGE("static image MakeFromEncoded fail! imageSource: %{private}s", imageSource.ToString().c_str());
158 taskExecutor->PostTask(
159 [failedCallback, imageSource] { failedCallback(imageSource); }, TaskExecutor::TaskType::UI);
160 return;
161 }
162 auto image = ImageProvider::ResizeSkImage(rawImage, imageSource.GetSrc(), imageSize, forceResize);
163 auto callback =
164 [successCallback, imageSource, taskExecutor, imageCache, imageSize](flutter::SkiaGPUObject<SkImage> image) {
165 auto canvasImage = flutter::CanvasImage::Create();
166 canvasImage->set_image(std::move(image));
167 if (imageCache) {
168 LOGD("cache image key: %{public}s", GenerateCacheKey(imageSource, imageSize).c_str());
169 imageCache->CacheImage(
170 GenerateCacheKey(imageSource, imageSize),
171 std::make_shared<CachedImage>(canvasImage));
172 }
173 taskExecutor->PostTask(
174 [ successCallback, imageSource, canvasImage ] {
175 successCallback(imageSource, canvasImage);
176 },
177 TaskExecutor::TaskType::UI);
178 };
179 ImageProvider::UploadImageToGPUForRender(image, callback, renderTaskHolder);
180 };
181 if (syncMode) {
182 task();
183 return;
184 }
185 uploadForPaintTask_ = CancelableTask(std::move(task));
186 BackgroundTaskExecutor::GetInstance().PostTask(uploadForPaintTask_);
187 }
188
CancelBackgroundTasks()189 bool StaticImageObject::CancelBackgroundTasks()
190 {
191 return uploadForPaintTask_ ? uploadForPaintTask_.Cancel(false) : false;
192 }
193
UploadToGpuForRender(const WeakPtr<PipelineContext> context,RefPtr<FlutterRenderTaskHolder> & renderTaskHolder,UploadSuccessCallback successCallback,FailedCallback failedCallback,Size imageSize,bool forceResize,bool syncMode)194 void AnimatedImageObject::UploadToGpuForRender(
195 const WeakPtr<PipelineContext> context,
196 RefPtr<FlutterRenderTaskHolder>& renderTaskHolder,
197 UploadSuccessCallback successCallback,
198 FailedCallback failedCallback,
199 Size imageSize,
200 bool forceResize,
201 bool syncMode)
202 {
203 if (!animatedPlayer_ && skData_) {
204 auto codec = SkCodec::MakeFromData(skData_);
205 int32_t dstWidth = -1;
206 int32_t dstHeight = -1;
207 if (forceResize) {
208 dstWidth = static_cast<int32_t>(imageSize.Width() + 0.5);
209 dstHeight = static_cast<int32_t>(imageSize.Height() + 0.5);
210 }
211 animatedPlayer_ = MakeRefPtr<AnimatedImagePlayer>(
212 imageSource_,
213 successCallback,
214 context,
215 renderTaskHolder->ioManager,
216 renderTaskHolder->unrefQueue,
217 std::move(codec),
218 dstWidth,
219 dstHeight);
220 ClearData();
221 } else if (animatedPlayer_ && forceResize && imageSize.IsValid()) {
222 LOGI("animated player has been construced, forceResize: %{public}s", imageSize.ToString().c_str());
223 int32_t dstWidth = static_cast<int32_t>(imageSize.Width() + 0.5);
224 int32_t dstHeight = static_cast<int32_t>(imageSize.Height() + 0.5);
225 animatedPlayer_->SetTargetSize(dstWidth, dstHeight);
226 } else if (!animatedPlayer_ && !skData_) {
227 LOGE("animated player is not constructed and image data is null, can not construct animated player!");
228 } else if (animatedPlayer_ && !forceResize) {
229 LOGI("animated player has been construced, do nothing!");
230 }
231 }
232
PerformLayoutImageObject(RefPtr<RenderImage> image)233 void PixelMapImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
234 {
235 image->PerformLayoutPixmap();
236 }
237
MeasureForImage(RefPtr<RenderImage> image)238 Size PixelMapImageObject::MeasureForImage(RefPtr<RenderImage> image)
239 {
240 return image->MeasureForPixmap();
241 }
242
243 } // namespace OHOS::Ace