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 #include "drawing/engine_adapter/skia_adapter/skia_data.h"
19
20 #include "base/thread/background_task_executor.h"
21 #include "core/common/container.h"
22 #include "core/components/image/render_image.h"
23 #include "core/image/image_cache.h"
24 #include "core/image/image_compressor.h"
25
26 namespace OHOS::Ace {
27
GenerateCacheKey(const ImageSourceInfo & srcInfo,Size targetImageSize)28 std::string ImageObject::GenerateCacheKey(const ImageSourceInfo& srcInfo, Size targetImageSize)
29 {
30 return srcInfo.GetKey() + std::to_string(static_cast<int32_t>(targetImageSize.Width())) +
31 std::to_string(static_cast<int32_t>(targetImageSize.Height()));
32 }
33
BuildImageObject(ImageSourceInfo source,const RefPtr<PipelineBase> context,const std::shared_ptr<RSData> & rsData,bool useSkiaSvg)34 RefPtr<ImageObject> ImageObject::BuildImageObject(
35 ImageSourceInfo source, const RefPtr<PipelineBase> context, const std::shared_ptr<RSData>& rsData, bool useSkiaSvg)
36 {
37 // build svg image object.
38 if (source.IsSvg()) {
39 #ifdef NG_BUILD
40 return nullptr;
41 #else
42 if (rsData == nullptr) {
43 return nullptr;
44 }
45 auto skData = SkData::MakeWithoutCopy(rsData->GetData(), rsData->GetSize());
46 const auto svgStream = std::make_unique<SkMemoryStream>(skData);
47 if (!svgStream) {
48 return nullptr;
49 }
50 auto color = source.GetFillColor();
51 if (!useSkiaSvg) {
52 return Ace::GetImageSvgDomObj(source, svgStream, context, color);
53 } else {
54 uint64_t colorValue = 0;
55 if (color.has_value()) {
56 colorValue = color.value().GetValue();
57 // skia svg relies on the 32th bit to determine whether or not to use the color we set.
58 colorValue = colorValue | (static_cast<int64_t>(0b1) << 32);
59 }
60 auto skiaDom = SkSVGDOM::MakeFromStream(*svgStream, colorValue);
61 return skiaDom ? MakeRefPtr<SvgSkiaImageObject>(source, Size(), 1, skiaDom) : nullptr;
62 }
63 #endif
64 }
65
66 // build normal pixel image object.
67 if (rsData == nullptr) {
68 return nullptr;
69 }
70 auto skData = SkData::MakeWithoutCopy(rsData->GetData(), rsData->GetSize());
71 auto codec = SkCodec::MakeFromData(skData);
72 int32_t totalFrames = 1;
73 Size imageSize;
74 if (codec) {
75 totalFrames = codec->getFrameCount();
76 switch (codec->getOrigin()) {
77 case SkEncodedOrigin::kLeftTop_SkEncodedOrigin:
78 case SkEncodedOrigin::kRightTop_SkEncodedOrigin:
79 case SkEncodedOrigin::kRightBottom_SkEncodedOrigin:
80 case SkEncodedOrigin::kLeftBottom_SkEncodedOrigin:
81 imageSize.SetSize(Size(codec->dimensions().fHeight, codec->dimensions().fWidth));
82 break;
83 default:
84 imageSize.SetSize(Size(codec->dimensions().fWidth, codec->dimensions().fHeight));
85 }
86 }
87 if (totalFrames == 1) {
88 return MakeRefPtr<StaticImageObject>(source, imageSize, totalFrames, rsData);
89 } else {
90 return CreateAnimatedImageObject(source, imageSize, totalFrames, rsData);
91 }
92 }
93
MeasureForImage(RefPtr<RenderImage> image)94 Size ImageObject::MeasureForImage(RefPtr<RenderImage> image)
95 {
96 return image->MeasureForNormalImage();
97 }
98
PerformLayoutImageObject(RefPtr<RenderImage> image)99 void SvgImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image)
100 {
101 image->PerformLayoutSvgImage();
102 }
103
MeasureForImage(RefPtr<RenderImage> image)104 Size SvgImageObject::MeasureForImage(RefPtr<RenderImage> image)
105 {
106 return image->MeasureForSvgImage();
107 }
108
PerformLayoutImageObject(RefPtr<RenderImage> image)109 void SvgSkiaImageObject::PerformLayoutImageObject(RefPtr<RenderImage> image) {}
110
MeasureForImage(RefPtr<RenderImage> image)111 Size SvgSkiaImageObject::MeasureForImage(RefPtr<RenderImage> image)
112 {
113 return image->MeasureForSvgImage();
114 }
115
UploadToGpuForRender(const WeakPtr<PipelineBase> & context,const UploadSuccessCallback & successCallback,const FailedCallback & failedCallback,const Size & imageSize,bool forceResize,bool syncMode)116 void StaticImageObject::UploadToGpuForRender(const WeakPtr<PipelineBase>& context,
117 const UploadSuccessCallback& successCallback,
118 const FailedCallback& failedCallback, const Size& imageSize, bool forceResize, bool syncMode)
119 {
120 auto task = [context, successCallback, failedCallback, imageSize, forceResize, rsData = data_,
121 imageSource = imageSource_, id = Container::CurrentId()]() mutable {
122 ContainerScope scope(id);
123 auto pipelineContext = context.Upgrade();
124 if (!pipelineContext) {
125 LOGE("pipeline context has been released.");
126 return;
127 }
128 auto taskExecutor = pipelineContext->GetTaskExecutor();
129 if (!taskExecutor) {
130 LOGE("task executor is null.");
131 return;
132 }
133
134 auto key = GenerateCacheKey(imageSource, imageSize);
135 // is already uploaded
136 if (!ImageProvider::TryUploadingImage(key, successCallback, failedCallback)) {
137 LOGI("other thread is uploading same image to gpu : %{private}s", imageSource.ToString().c_str());
138 return;
139 }
140
141 RefPtr<NG::CanvasImage> cachedFlutterImage;
142 auto imageCache = pipelineContext->GetImageCache();
143 if (imageCache) {
144 auto cachedImage = imageCache->GetCacheImage(key);
145 if (cachedImage) {
146 auto rsImage = cachedImage->imagePtr;
147 cachedFlutterImage = NG::CanvasImage::Create(&rsImage);
148 }
149 }
150
151 // found cached image obj (can be rendered)
152 if (cachedFlutterImage) {
153 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, cachedFlutterImage);
154 return;
155 }
156
157 auto callback = [successCallback, imageSource, taskExecutor, imageCache, imageSize, key,
158 id = Container::CurrentId()](
159 std::shared_ptr<RSImage> image, std::shared_ptr<RSData> compressData) {
160
161 if (!image && !compressData.get()) {
162 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
163 "Image data may be broken or absent in upload callback.");
164 }
165 ContainerScope scope(id);
166 std::shared_ptr<RSImage> rsImage = image;
167 auto canvasImage = NG::CanvasImage::Create(&rsImage);
168 int32_t width = static_cast<int32_t>(imageSize.Width() + 0.5);
169 int32_t height = static_cast<int32_t>(imageSize.Height() + 0.5);
170 canvasImage->SetRawCompressData(&compressData, width, height);
171
172 if (imageCache) {
173 imageCache->CacheImage(key, std::make_shared<CachedImage>(rsImage));
174 }
175 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, canvasImage);
176 };
177 // here skdata is origin pic, also have 'target size'
178 // if have skdata, means origin pic is rendered first time
179 // if no skdata, means origin pic has shown, and has been cleared
180 // we try to use small image or compressed image instead of origin pic.
181 std::shared_ptr<RSData> stripped;
182 if (ImageCompressor::GetInstance()->CanCompress()) {
183 // load compressed
184 auto compressedData = ImageProvider::LoadImageRawDataFromFileCache(pipelineContext, key, ".astc");
185 stripped = ImageCompressor::StripFileHeader(compressedData);
186 }
187 auto smallData = ImageProvider::LoadImageRawDataFromFileCache(pipelineContext, key);
188 if (smallData) {
189 rsData = smallData;
190 }
191
192 if (!rsData) {
193 rsData = ImageProvider::LoadImageRawData(imageSource, pipelineContext);
194 if (!rsData) {
195 LOGE("reload image data failed. imageSource: %{private}s", imageSource.ToString().c_str());
196 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
197 "Image data may be broken or absent, please check if image file or image data is valid.");
198 return;
199 }
200 }
201
202 // make lazy image from file
203 auto rawImage = std::make_shared<RSImage>();
204 bool result = rawImage->MakeFromEncoded(rsData);
205 if (!result) {
206 LOGE("static image MakeFromEncoded fail! imageSource: %{private}s", imageSource.ToString().c_str());
207 ImageProvider::ProccessUploadResult(taskExecutor, imageSource, imageSize, nullptr,
208 "Image data may be broken, please check if image file or image data is broken.");
209 return;
210 }
211 std::shared_ptr<RSImage> image;
212 if (smallData) {
213 image = rawImage;
214 } else {
215 image = ImageProvider::ResizeDrawingImage(rawImage, imageSource.GetSrc(), imageSize, forceResize);
216 }
217 ImageProvider::UploadImageToGPUForRender(pipelineContext, image, stripped, callback, key);
218 rsData = nullptr;
219 };
220 if (syncMode) {
221 task();
222 return;
223 }
224 uploadForPaintTask_ = CancelableTask(std::move(task));
225 BackgroundTaskExecutor::GetInstance().PostTask(uploadForPaintTask_);
226 }
227
CancelBackgroundTasks()228 bool StaticImageObject::CancelBackgroundTasks()
229 {
230 return uploadForPaintTask_ ? uploadForPaintTask_.Cancel(false) : false;
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
244