1 /*
2 * Copyright (c) 2021-2023 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 #ifdef USE_ROSEN_DRAWING
19 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
20 #endif
21
22 #include "base/thread/background_task_executor.h"
23 #include "base/utils/utils.h"
24 #include "core/common/container.h"
25 #include "core/common/container_scope.h"
26 #include "core/components/image/render_image.h"
27 #include "core/components_ng/render/canvas_image.h"
28 #include "core/image/flutter_image_cache.h"
29 #include "core/image/image_compressor.h"
30
31 #ifdef APNG_IMAGE_SUPPORT
32 #include "core/image/apng/apng_image_decoder.h"
33 #include "core/image/apng/apng_image_object.h"
34 #endif
35
36 namespace OHOS::Ace {
37
GenerateCacheKey(const ImageSourceInfo & srcInfo,Size targetImageSize)38 std::string ImageObject::GenerateCacheKey(const ImageSourceInfo& srcInfo, Size targetImageSize)
39 {
40 return srcInfo.GetKey() + std::to_string(static_cast<int32_t>(targetImageSize.Width())) +
41 std::to_string(static_cast<int32_t>(targetImageSize.Height()));
42 }
43
44 #ifndef USE_ROSEN_DRAWING
BuildImageObject(ImageSourceInfo source,const RefPtr<PipelineBase> context,const sk_sp<SkData> & skData,bool useSkiaSvg)45 RefPtr<ImageObject> ImageObject::BuildImageObject(
46 ImageSourceInfo source, const RefPtr<PipelineBase> context, const sk_sp<SkData>& skData, bool useSkiaSvg)
47 #else
48 RefPtr<ImageObject> ImageObject::BuildImageObject(
49 ImageSourceInfo source, const RefPtr<PipelineBase> context, const std::shared_ptr<RSData>& rsData, bool useSkiaSvg)
50 #endif
51 {
52 // build svg image object.
53 if (source.IsSvg()) {
54 #ifdef NG_BUILD
55 return nullptr;
56 #else
57 #ifndef USE_ROSEN_DRAWING
58 const auto svgStream = std::make_unique<SkMemoryStream>(skData);
59 #else
60 if (rsData == nullptr) {
61 return nullptr;
62 }
63 auto skData = rsData->GetImpl<Rosen::Drawing::SkiaData>()->GetSkData();
64 const auto svgStream = std::make_unique<SkMemoryStream>(skData);
65 #endif
66 if (!svgStream) {
67 return nullptr;
68 }
69 auto color = source.GetFillColor();
70 if (!useSkiaSvg) {
71 return Ace::GetImageSvgDomObj(source, svgStream, context, color);
72 } else {
73 uint64_t colorValue = 0;
74 if (color.has_value()) {
75 colorValue = color.value().GetValue();
76 // skia svg relies on the 32th bit to determine whether or not to use the color we set.
77 colorValue = colorValue | (static_cast<int64_t>(0b1) << 32);
78 }
79 auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, colorValue);
80 return skiaDom ? MakeRefPtr<SvgSkiaImageObject>(source, Size(), 1, skiaDom) : nullptr;
81 }
82 #endif
83 }
84
85 // if is png or apng check
86 #ifdef APNG_IMAGE_SUPPORT
87 if (source.isPng()) {
88 #ifndef USE_ROSEN_DRAWING
89 auto apngDecoder = AceType::MakeRefPtr<PNGImageDecoder>(skData);
90 #else
91 auto apngDecoder = AceType::MakeRefPtr<PNGImageDecoder>(data);
92 #endif
93 if (apngDecoder && apngDecoder->isApng()) {
94 if (!apngDecoder->DecodeImage()) {
95 return nullptr;
96 }
97
98 Size imageSize = apngDecoder->GetImageSize();
99 uint32_t frameCount = apngDecoder->GetFrameCount();
100 #ifndef USE_ROSEN_DRAWING
101 return MakeRefPtr<ApngImageObject>(source, imageSize, frameCount, skData, apngDecoder);
102 #else
103 return MakeRefPtr<ApngImageObject>(source, imageSize, frameCount, data, apngDecoder);
104 #endif
105 }
106 }
107 #endif
108
109 // build normal pixel image object.
110 #ifndef USE_ROSEN_DRAWING
111 auto codec = SkCodec::MakeFromData(skData);
112 #else
113 if (rsData == nullptr) {
114 return nullptr;
115 }
116 auto skData = rsData->GetImpl<Rosen::Drawing::SkiaData>()->GetSkData();
117 auto codec = SkCodec::MakeFromData(skData);
118 #endif
119 int32_t totalFrames = 1;
120 Size imageSize;
121 if (codec) {
122 totalFrames = codec->getFrameCount();
123 switch (codec->getOrigin()) {
124 case SkEncodedOrigin::kLeftTop_SkEncodedOrigin:
125 case SkEncodedOrigin::kRightTop_SkEncodedOrigin:
126 case SkEncodedOrigin::kRightBottom_SkEncodedOrigin:
127 case SkEncodedOrigin::kLeftBottom_SkEncodedOrigin:
128 imageSize.SetSize(Size(codec->dimensions().fHeight, codec->dimensions().fWidth));
129 break;
130 default:
131 imageSize.SetSize(Size(codec->dimensions().fWidth, codec->dimensions().fHeight));
132 }
133 }
134 #ifndef USE_ROSEN_DRAWING
135 if (totalFrames == 1) {
136 return MakeRefPtr<StaticImageObject>(source, imageSize, totalFrames, skData);
137 } else {
138 return CreateAnimatedImageObject(source, imageSize, totalFrames, skData);
139 }
140 #else
141 if (totalFrames == 1) {
142 return MakeRefPtr<StaticImageObject>(source, imageSize, totalFrames, rsData);
143 } else {
144 return CreateAnimatedImageObject(source, imageSize, totalFrames, rsData);
145 }
146 #endif
147 }
148
MeasureForImage(RefPtr<RenderImage> image)149 Size ImageObject::MeasureForImage(RefPtr<RenderImage> image)
150 {
151 return image->MeasureForNormalImage();
152 }
153
PerformLayoutImageObject(RefPtr<RenderImage> image)154 void SvgImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
155 {
156 image->PerformLayoutSvgImage();
157 }
158
MeasureForImage(RefPtr<RenderImage> image)159 Size SvgImageObject::MeasureForImage(RefPtr<RenderImage> image)
160 {
161 return image->MeasureForSvgImage();
162 }
163
PerformLayoutImageObject(RefPtr<RenderImage> image)164 void SvgSkiaImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image) {}
165
MeasureForImage(RefPtr<RenderImage> image)166 Size SvgSkiaImageObject::MeasureForImage(RefPtr<RenderImage> image)
167 {
168 return image->MeasureForSvgImage();
169 }
170
UploadToGpuForRender(const WeakPtr<PipelineBase> & context,const UploadSuccessCallback & successCallback,const FailedCallback & failedCallback,const Size & imageSize,bool forceResize,bool syncMode)171 void StaticImageObject::UploadToGpuForRender(const WeakPtr<PipelineBase>& context,
172 const UploadSuccessCallback& successCallback,
173 const FailedCallback& failedCallback, const Size& imageSize, bool forceResize, bool syncMode)
174 {
175 #ifndef USE_ROSEN_DRAWING
176 auto task = [context, successCallback, failedCallback, imageSize, forceResize, skData = skData_,
177 #else
178 auto task = [context, successCallback, failedCallback, imageSize, forceResize, rsData = data_,
179 #endif
180 imageSource = imageSource_, id = Container::CurrentId()]() mutable {
181 ContainerScope scope(id);
182 auto pipelineContext = context.Upgrade();
183 if (!pipelineContext) {
184 LOGE("pipeline context has been released.");
185 return;
186 }
187 auto taskExecutor = pipelineContext->GetTaskExecutor();
188 if (!taskExecutor) {
189 LOGE("task executor is null.");
190 return;
191 }
192
193 auto key = GenerateCacheKey(imageSource, imageSize);
194 // is already uploaded
195 if (!ImageProvider::TryUploadingImage(key, successCallback, failedCallback)) {
196 LOGI("other thread is uploading same image to gpu : %{public}s", imageSource.ToString().c_str());
197 return;
198 }
199
200 RefPtr<NG::CanvasImage> cachedFlutterImage;
201 auto imageCache = pipelineContext->GetImageCache();
202 if (imageCache) {
203 auto cachedImage = imageCache->GetCacheImage(key);
204 if (cachedImage) {
205 #ifndef USE_ROSEN_DRAWING
206 auto skImage = cachedImage->imagePtr;
207 cachedFlutterImage = NG::CanvasImage::Create(&skImage);
208 #else
209 auto rsImage = cachedImage->imagePtr;
210 cachedFlutterImage = NG::CanvasImage::Create(&rsImage);
211 #endif
212 }
213 }
214
215 // found cached image obj (can be rendered)
216 if (cachedFlutterImage) {
217 LOGD("get cached image success: %{public}s", key.c_str());
218 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, cachedFlutterImage);
219 return;
220 }
221
222 auto callback = [successCallback, imageSource, taskExecutor, imageCache, imageSize, key,
223 id = Container::CurrentId()](
224 #ifndef USE_ROSEN_DRAWING
225 sk_sp<SkImage> image, sk_sp<SkData> compressData) {
226 #else
227 std::shared_ptr<RSImage> image, std::shared_ptr<RSData> compressData) {
228 #endif
229
230 if (!image && !compressData.get()) {
231 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
232 "Image data may be broken or absent in upload callback.");
233 }
234 ContainerScope scope(id);
235 #ifndef USE_ROSEN_DRAWING
236 sk_sp<SkImage> skImage = image;
237 auto canvasImage = NG::CanvasImage::Create(&skImage);
238 #else
239 std::shared_ptr<RSImage> rsImage = image;
240 auto canvasImage = NG::CanvasImage::Create(&rsImage);
241 #endif
242 int32_t width = static_cast<int32_t>(imageSize.Width() + 0.5);
243 int32_t height = static_cast<int32_t>(imageSize.Height() + 0.5);
244 canvasImage->SetRawCompressData(&compressData, width, height);
245
246 if (imageCache) {
247 LOGD("cache image key: %{public}s", key.c_str());
248 #ifndef USE_ROSEN_DRAWING
249 imageCache->CacheImage(key, std::make_shared<CachedImage>(skImage));
250 #else
251 imageCache->CacheImage(key, std::make_shared<CachedImage>(rsImage));
252 #endif
253 }
254 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, canvasImage);
255 };
256 // here skdata is origin pic, also have 'target size'
257 // if have skdata, means origin pic is rendered first time
258 // if no skdata, means origin pic has shown, and has been cleared
259 // we try to use small image or compressed image instead of origin pic.
260 #ifndef USE_ROSEN_DRAWING
261 sk_sp<SkData> stripped;
262 #else
263 std::shared_ptr<RSData> stripped;
264 #endif
265 if (ImageCompressor::GetInstance()->CanCompress()) {
266 // load compressed
267 auto compressedData = ImageProvider::LoadImageRawDataFromFileCache(pipelineContext, key, ".astc");
268 stripped = ImageCompressor::StripFileHeader(compressedData);
269 }
270 auto smallData = ImageProvider::LoadImageRawDataFromFileCache(pipelineContext, key);
271 if (smallData) {
272 #ifndef USE_ROSEN_DRAWING
273 skData = smallData;
274 #else
275 rsData = smallData;
276 #endif
277 }
278
279 #ifndef USE_ROSEN_DRAWING
280 if (!skData) {
281 LOGD("reload sk data");
282 skData = ImageProvider::LoadImageRawData(imageSource, pipelineContext);
283 if (!skData) {
284 #else
285 if (!rsData) {
286 LOGD("reload rs data");
287 rsData = ImageProvider::LoadImageRawData(imageSource, pipelineContext);
288 if (!rsData) {
289 #endif
290 LOGE("reload image data failed. imageSource: %{private}s", imageSource.ToString().c_str());
291 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
292 "Image data may be broken or absent, please check if image file or image data is valid.");
293 return;
294 }
295 }
296
297 // make lazy image from file
298 #ifndef USE_ROSEN_DRAWING
299 auto rawImage = SkImage::MakeFromEncoded(skData);
300 #else
301 auto skData = rsData->GetImpl<Rosen::Drawing::SkiaData>()->GetSkData();
302 auto rawImage = SkImage::MakeFromEncoded(skData);
303 #endif
304 if (!rawImage) {
305 LOGE("static image MakeFromEncoded fail! imageSource: %{private}s", imageSource.ToString().c_str());
306 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
307 "Image data may be broken, please check if image file or image data is broken.");
308 return;
309 }
310 #ifndef USE_ROSEN_DRAWING
311 sk_sp<SkImage> image;
312 if (smallData) {
313 image = rawImage;
314 } else {
315 image = ImageProvider::ResizeSkImage(rawImage, imageSource.GetSrc(), imageSize, forceResize);
316 }
317 ImageProvider::UploadImageToGPUForRender(pipelineContext, image, stripped, callback, key);
318 skData = nullptr;
319 #else
320 std::shared_ptr<RSImage> image;
321 auto rsRawImage = std::make_shared<RSImage>(&rawImage);
322 if (smallData) {
323 image = rsRawImage;
324 } else {
325 image = ImageProvider::ResizeDrawingImage(rsRawImage, imageSource.GetSrc(), imageSize, forceResize);
326 }
327 ImageProvider::UploadImageToGPUForRender(pipelineContext, image, stripped, callback, key);
328 rsData = nullptr;
329 #endif
330 };
331 if (syncMode) {
332 task();
333 return;
334 }
335 uploadForPaintTask_ = CancelableTask(std::move(task));
336 BackgroundTaskExecutor::GetInstance().PostTask(uploadForPaintTask_);
337 }
338
339 bool StaticImageObject::CancelBackgroundTasks()
340 {
341 return uploadForPaintTask_ ? uploadForPaintTask_.Cancel(false) : false;
342 }
343
344 void PixelMapImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
345 {
346 image->PerformLayoutPixmap();
347 }
348
349 Size PixelMapImageObject::MeasureForImage(RefPtr<RenderImage> image)
350 {
351 return image->MeasureForPixmap();
352 }
353
354 } // namespace OHOS::Ace
355