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