1 /*
2 * Copyright (C) 2006 Samuel Weinig (sam.weinig@gmail.com)
3 * Copyright (C) 2004, 2005, 2006, 2008 Apple Inc. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "third_party/blink/renderer/platform/graphics/bitmap_image.h"
28
29 #include <algorithm>
30 #include <memory>
31 #include <utility>
32
33 #include "base/memory/scoped_refptr.h"
34 #include "base/metrics/histogram_macros.h"
35 #include "third_party/blink/renderer/platform/geometry/float_rect.h"
36 #include "third_party/blink/renderer/platform/graphics/bitmap_image_metrics.h"
37 #include "third_party/blink/renderer/platform/graphics/dark_mode_image_classifier.h"
38 #include "third_party/blink/renderer/platform/graphics/deferred_image_decoder.h"
39 #include "third_party/blink/renderer/platform/graphics/image_observer.h"
40 #include "third_party/blink/renderer/platform/graphics/paint/paint_canvas.h"
41 #include "third_party/blink/renderer/platform/graphics/paint/paint_flags.h"
42 #include "third_party/blink/renderer/platform/graphics/paint/paint_image.h"
43 #include "third_party/blink/renderer/platform/graphics/skia/skia_utils.h"
44 #include "third_party/blink/renderer/platform/graphics/static_bitmap_image.h"
45 #include "third_party/blink/renderer/platform/instrumentation/tracing/trace_event.h"
46 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
47 #include "third_party/blink/renderer/platform/scheduler/public/thread_scheduler.h"
48 #include "third_party/blink/renderer/platform/timer.h"
49 #include "third_party/blink/renderer/platform/wtf/assertions.h"
50 #include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
51
52 namespace blink {
53 namespace {
54
55 const int kMinImageSizeForClassification1D = 24;
56 const int kMaxImageSizeForClassification1D = 100;
57
58 } // namespace
59
GetRepetitionCountWithPolicyOverride(int actual_count,ImageAnimationPolicy policy)60 int GetRepetitionCountWithPolicyOverride(int actual_count,
61 ImageAnimationPolicy policy) {
62 if (actual_count == kAnimationNone ||
63 policy == kImageAnimationPolicyNoAnimation) {
64 return kAnimationNone;
65 }
66
67 if (actual_count == kAnimationLoopOnce ||
68 policy == kImageAnimationPolicyAnimateOnce) {
69 return kAnimationLoopOnce;
70 }
71
72 return actual_count;
73 }
74
BitmapImage(ImageObserver * observer,bool is_multipart)75 BitmapImage::BitmapImage(ImageObserver* observer, bool is_multipart)
76 : Image(observer, is_multipart),
77 animation_policy_(kImageAnimationPolicyAllowed),
78 all_data_received_(false),
79 have_size_(false),
80 size_available_(false),
81 have_frame_count_(false),
82 repetition_count_status_(kUnknown),
83 repetition_count_(kAnimationNone),
84 frame_count_(0) {}
85
~BitmapImage()86 BitmapImage::~BitmapImage() {}
87
CurrentFrameHasSingleSecurityOrigin() const88 bool BitmapImage::CurrentFrameHasSingleSecurityOrigin() const {
89 return true;
90 }
91
DestroyDecodedData()92 void BitmapImage::DestroyDecodedData() {
93 cached_frame_ = PaintImage();
94 NotifyMemoryChanged();
95 }
96
Data()97 scoped_refptr<SharedBuffer> BitmapImage::Data() {
98 return decoder_ ? decoder_->Data() : nullptr;
99 }
100
NotifyMemoryChanged()101 void BitmapImage::NotifyMemoryChanged() {
102 if (GetImageObserver())
103 GetImageObserver()->DecodedSizeChangedTo(this, TotalFrameBytes());
104 }
105
TotalFrameBytes()106 size_t BitmapImage::TotalFrameBytes() {
107 if (cached_frame_)
108 return static_cast<size_t>(Size().Area()) * sizeof(ImageFrame::PixelData);
109 return 0u;
110 }
111
PaintImageForTesting()112 PaintImage BitmapImage::PaintImageForTesting() {
113 return CreatePaintImage();
114 }
115
CreatePaintImage()116 PaintImage BitmapImage::CreatePaintImage() {
117 sk_sp<PaintImageGenerator> generator =
118 decoder_ ? decoder_->CreateGenerator() : nullptr;
119 if (!generator)
120 return PaintImage();
121
122 auto completion_state = all_data_received_
123 ? PaintImage::CompletionState::DONE
124 : PaintImage::CompletionState::PARTIALLY_DONE;
125 auto builder =
126 CreatePaintImageBuilder()
127 .set_paint_image_generator(std::move(generator))
128 .set_repetition_count(GetRepetitionCountWithPolicyOverride(
129 RepetitionCount(), animation_policy_))
130 .set_is_high_bit_depth(decoder_->ImageIsHighBitDepth())
131 .set_completion_state(completion_state)
132 .set_reset_animation_sequence_id(reset_animation_sequence_id_);
133
134 return builder.TakePaintImage();
135 }
136
UpdateSize() const137 void BitmapImage::UpdateSize() const {
138 if (!size_available_ || have_size_ || !decoder_)
139 return;
140
141 size_ = decoder_->FrameSizeAtIndex(0);
142 if (decoder_->OrientationAtIndex(0).UsesWidthAsHeight())
143 size_respecting_orientation_ = size_.TransposedSize();
144 else
145 size_respecting_orientation_ = size_;
146 have_size_ = true;
147 }
148
Size() const149 IntSize BitmapImage::Size() const {
150 UpdateSize();
151 return size_;
152 }
153
SizeRespectingOrientation() const154 IntSize BitmapImage::SizeRespectingOrientation() const {
155 UpdateSize();
156 return size_respecting_orientation_;
157 }
158
HasDefaultOrientation() const159 bool BitmapImage::HasDefaultOrientation() const {
160 ImageOrientation orientation = CurrentFrameOrientation();
161 return orientation == kDefaultImageOrientation;
162 }
163
GetHotSpot(IntPoint & hot_spot) const164 bool BitmapImage::GetHotSpot(IntPoint& hot_spot) const {
165 return decoder_ && decoder_->HotSpot(hot_spot);
166 }
167
168 // We likely don't need to confirm that this is the first time all data has
169 // been received as a way to avoid reporting the UMA multiple times for the
170 // same image. However, we err on the side of caution.
ShouldReportByteSizeUMAs(bool data_now_completely_received)171 bool BitmapImage::ShouldReportByteSizeUMAs(bool data_now_completely_received) {
172 if (!decoder_)
173 return false;
174 // Ensures that refactoring to check truthiness of ByteSize() method is
175 // equivalent to the previous use of Data() and does not mess up UMAs.
176 DCHECK_EQ(!decoder_->ByteSize(), !decoder_->Data());
177 return !all_data_received_ && data_now_completely_received &&
178 decoder_->ByteSize() && IsSizeAvailable();
179 }
180
SetData(scoped_refptr<SharedBuffer> data,bool all_data_received)181 Image::SizeAvailability BitmapImage::SetData(scoped_refptr<SharedBuffer> data,
182 bool all_data_received) {
183 if (!data)
184 return kSizeAvailable;
185
186 int length = data->size();
187 if (!length)
188 return kSizeAvailable;
189
190 if (decoder_) {
191 decoder_->SetData(std::move(data), all_data_received);
192 return DataChanged(all_data_received);
193 }
194
195 bool has_enough_data = ImageDecoder::HasSufficientDataToSniffImageType(*data);
196 decoder_ = DeferredImageDecoder::Create(std::move(data), all_data_received,
197 ImageDecoder::kAlphaPremultiplied,
198 ColorBehavior::Tag());
199 // If we had enough data but couldn't create a decoder, it implies a decode
200 // failure.
201 if (has_enough_data && !decoder_)
202 return kSizeAvailable;
203 return DataChanged(all_data_received);
204 }
205
206 // Return the image density in 0.01 "bits per pixel" rounded to the nearest
207 // integer.
ImageDensityInCentiBpp(IntSize size,size_t image_size_bytes)208 static inline uint64_t ImageDensityInCentiBpp(IntSize size,
209 size_t image_size_bytes) {
210 uint64_t image_area = static_cast<uint64_t>(size.Width()) * size.Height();
211 return (static_cast<uint64_t>(image_size_bytes) * 100 * 8 + image_area / 2) /
212 image_area;
213 }
214
DataChanged(bool all_data_received)215 Image::SizeAvailability BitmapImage::DataChanged(bool all_data_received) {
216 TRACE_EVENT0("blink", "BitmapImage::dataChanged");
217
218 // If the data was updated, clear the |cached_frame_| to push it to the
219 // compositor thread. Its necessary to clear the frame since more data
220 // requires a new PaintImageGenerator instance.
221 cached_frame_ = PaintImage();
222
223 // Report the image density metric right after we received all the data. The
224 // SetData() call on the decoder_ (if there is one) should have decoded the
225 // images and we should know the image size at this point.
226 if (ShouldReportByteSizeUMAs(all_data_received) &&
227 decoder_->FilenameExtension() == "jpg") {
228 BitmapImageMetrics::CountImageJpegDensity(
229 std::min(Size().Width(), Size().Height()),
230 ImageDensityInCentiBpp(Size(), decoder_->ByteSize()),
231 decoder_->ByteSize());
232 }
233
234 // Feed all the data we've seen so far to the image decoder.
235 all_data_received_ = all_data_received;
236 have_frame_count_ = false;
237
238 return IsSizeAvailable() ? kSizeAvailable : kSizeUnavailable;
239 }
240
HasColorProfile() const241 bool BitmapImage::HasColorProfile() const {
242 return decoder_ && decoder_->HasEmbeddedColorProfile();
243 }
244
FilenameExtension() const245 String BitmapImage::FilenameExtension() const {
246 return decoder_ ? decoder_->FilenameExtension() : String();
247 }
248
Draw(cc::PaintCanvas * canvas,const PaintFlags & flags,const FloatRect & dst_rect,const FloatRect & src_rect,RespectImageOrientationEnum should_respect_image_orientation,ImageClampingMode clamp_mode,ImageDecodingMode decode_mode)249 void BitmapImage::Draw(
250 cc::PaintCanvas* canvas,
251 const PaintFlags& flags,
252 const FloatRect& dst_rect,
253 const FloatRect& src_rect,
254 RespectImageOrientationEnum should_respect_image_orientation,
255 ImageClampingMode clamp_mode,
256 ImageDecodingMode decode_mode) {
257 TRACE_EVENT0("skia", "BitmapImage::draw");
258
259 PaintImage image = PaintImageForCurrentFrame();
260 if (!image)
261 return; // It's too early and we don't have an image yet.
262
263 auto paint_image_decoding_mode = ToPaintImageDecodingMode(decode_mode);
264 if (image.decoding_mode() != paint_image_decoding_mode) {
265 image = PaintImageBuilder::WithCopy(std::move(image))
266 .set_decoding_mode(paint_image_decoding_mode)
267 .TakePaintImage();
268 }
269
270 FloatRect adjusted_src_rect = src_rect;
271 adjusted_src_rect.Intersect(SkRect::MakeWH(image.width(), image.height()));
272
273 if (adjusted_src_rect.IsEmpty() || dst_rect.IsEmpty())
274 return; // Nothing to draw.
275
276 ImageOrientation orientation = kDefaultImageOrientation;
277 if (should_respect_image_orientation == kRespectImageOrientation)
278 orientation = CurrentFrameOrientation();
279
280 PaintCanvasAutoRestore auto_restore(canvas, false);
281 FloatRect adjusted_dst_rect = dst_rect;
282 if (orientation != kDefaultImageOrientation) {
283 canvas->save();
284
285 // ImageOrientation expects the origin to be at (0, 0)
286 canvas->translate(adjusted_dst_rect.X(), adjusted_dst_rect.Y());
287 adjusted_dst_rect.SetLocation(FloatPoint());
288
289 canvas->concat(AffineTransformToSkMatrix(
290 orientation.TransformFromDefault(adjusted_dst_rect.Size())));
291
292 if (orientation.UsesWidthAsHeight()) {
293 // The destination rect will have its width and height already reversed
294 // for the orientation of the image, as it was needed for page layout, so
295 // we need to reverse it back here.
296 adjusted_dst_rect =
297 FloatRect(adjusted_dst_rect.X(), adjusted_dst_rect.Y(),
298 adjusted_dst_rect.Height(), adjusted_dst_rect.Width());
299 }
300 }
301
302 uint32_t unique_id = image.GetSkImage()->uniqueID();
303 bool is_lazy_generated = image.IsLazyGenerated();
304 canvas->drawImageRect(std::move(image), adjusted_src_rect, adjusted_dst_rect,
305 &flags,
306 WebCoreClampingModeToSkiaRectConstraint(clamp_mode));
307
308 if (is_lazy_generated) {
309 TRACE_EVENT_INSTANT1(TRACE_DISABLED_BY_DEFAULT("devtools.timeline"),
310 "Draw LazyPixelRef", TRACE_EVENT_SCOPE_THREAD,
311 "LazyPixelRef", unique_id);
312 }
313
314 StartAnimation();
315 }
316
FrameCount()317 size_t BitmapImage::FrameCount() {
318 if (!have_frame_count_) {
319 frame_count_ = decoder_ ? decoder_->FrameCount() : 0;
320 have_frame_count_ = frame_count_ > 0;
321 }
322 return frame_count_;
323 }
324
HasVisibleImageSize(IntSize size)325 static inline bool HasVisibleImageSize(IntSize size) {
326 return (size.Width() > 1 || size.Height() > 1);
327 }
328
IsSizeAvailable()329 bool BitmapImage::IsSizeAvailable() {
330 if (size_available_)
331 return true;
332
333 size_available_ = decoder_ && decoder_->IsSizeAvailable();
334 if (size_available_ && HasVisibleImageSize(Size())) {
335 BitmapImageMetrics::CountDecodedImageType(decoder_->FilenameExtension());
336 if (decoder_->FilenameExtension() == "jpg") {
337 BitmapImageMetrics::CountImageOrientation(
338 decoder_->OrientationAtIndex(0).Orientation());
339 }
340 }
341
342 return size_available_;
343 }
344
PaintImageForCurrentFrame()345 PaintImage BitmapImage::PaintImageForCurrentFrame() {
346 if (cached_frame_)
347 return cached_frame_;
348
349 cached_frame_ = CreatePaintImage();
350
351 // Create the SkImage backing for this PaintImage here to ensure that copies
352 // of the PaintImage share the same SkImage. Skia's caching of the decoded
353 // output of this image is tied to the lifetime of the SkImage. So we create
354 // the SkImage here and cache the PaintImage to keep the decode alive in
355 // skia's cache.
356 cached_frame_.GetSkImage();
357 NotifyMemoryChanged();
358
359 return cached_frame_;
360 }
361
ImageForDefaultFrame()362 scoped_refptr<Image> BitmapImage::ImageForDefaultFrame() {
363 if (FrameCount() > 1) {
364 PaintImage paint_image = PaintImageForCurrentFrame();
365 if (!paint_image)
366 return nullptr;
367
368 if (paint_image.ShouldAnimate()) {
369 // To prevent the compositor from animating this image, we set the
370 // animation count to kAnimationNone. This makes the image essentially
371 // static.
372 paint_image = PaintImageBuilder::WithCopy(std::move(paint_image))
373 .set_repetition_count(kAnimationNone)
374 .TakePaintImage();
375 }
376 return StaticBitmapImage::Create(std::move(paint_image));
377 }
378
379 return Image::ImageForDefaultFrame();
380 }
381
CurrentFrameKnownToBeOpaque()382 bool BitmapImage::CurrentFrameKnownToBeOpaque() {
383 // If the image is animated, it is being animated by the compositor and we
384 // don't know what the current frame is.
385 // TODO(khushalsagar): We could say the image is opaque if none of the frames
386 // have alpha.
387 if (MaybeAnimated())
388 return false;
389
390 // We ask the decoder whether the image has alpha because in some cases the
391 // the correct value is known after decoding. The DeferredImageDecoder caches
392 // the accurate value from the decoded result.
393 const bool frame_has_alpha =
394 decoder_ ? decoder_->FrameHasAlphaAtIndex(PaintImage::kDefaultFrameIndex)
395 : true;
396 return !frame_has_alpha;
397 }
398
CurrentFrameIsComplete()399 bool BitmapImage::CurrentFrameIsComplete() {
400 return decoder_
401 ? decoder_->FrameIsReceivedAtIndex(PaintImage::kDefaultFrameIndex)
402 : false;
403 }
404
CurrentFrameIsLazyDecoded()405 bool BitmapImage::CurrentFrameIsLazyDecoded() {
406 // BitmapImage supports only lazy generated images.
407 return true;
408 }
409
CurrentFrameOrientation() const410 ImageOrientation BitmapImage::CurrentFrameOrientation() const {
411 return decoder_ ? decoder_->OrientationAtIndex(PaintImage::kDefaultFrameIndex)
412 : kDefaultImageOrientation;
413 }
414
RepetitionCount()415 int BitmapImage::RepetitionCount() {
416 if ((repetition_count_status_ == kUnknown) ||
417 ((repetition_count_status_ == kUncertain) && all_data_received_)) {
418 // Snag the repetition count. If |imageKnownToBeComplete| is false, the
419 // repetition count may not be accurate yet for GIFs; in this case the
420 // decoder will default to cAnimationLoopOnce, and we'll try and read
421 // the count again once the whole image is decoded.
422 repetition_count_ = decoder_ ? decoder_->RepetitionCount() : kAnimationNone;
423
424 // When requesting more than a single loop, repetition count is one less
425 // than the actual number of loops.
426 if (repetition_count_ > 0)
427 repetition_count_++;
428
429 repetition_count_status_ =
430 (all_data_received_ || repetition_count_ == kAnimationNone)
431 ? kCertain
432 : kUncertain;
433 }
434 return repetition_count_;
435 }
436
ResetAnimation()437 void BitmapImage::ResetAnimation() {
438 cached_frame_ = PaintImage();
439 reset_animation_sequence_id_++;
440 }
441
MaybeAnimated()442 bool BitmapImage::MaybeAnimated() {
443 if (FrameCount() > 1)
444 return true;
445
446 return decoder_ && decoder_->RepetitionCount() != kAnimationNone;
447 }
448
SetAnimationPolicy(ImageAnimationPolicy policy)449 void BitmapImage::SetAnimationPolicy(ImageAnimationPolicy policy) {
450 if (animation_policy_ == policy)
451 return;
452
453 animation_policy_ = policy;
454 ResetAnimation();
455 }
456
CheckTypeSpecificConditionsForDarkMode(const FloatRect & dest_rect,DarkModeImageClassifier * classifier)457 DarkModeClassification BitmapImage::CheckTypeSpecificConditionsForDarkMode(
458 const FloatRect& dest_rect,
459 DarkModeImageClassifier* classifier) {
460 if (dest_rect.Width() < kMinImageSizeForClassification1D ||
461 dest_rect.Height() < kMinImageSizeForClassification1D)
462 return DarkModeClassification::kApplyFilter;
463
464 if (dest_rect.Width() > kMaxImageSizeForClassification1D ||
465 dest_rect.Height() > kMaxImageSizeForClassification1D) {
466 return DarkModeClassification::kDoNotApplyFilter;
467 }
468
469 classifier->SetImageType(DarkModeImageClassifier::ImageType::kBitmap);
470
471 return DarkModeClassification::kNotClassified;
472 }
473
474 } // namespace blink
475