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