• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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