File: image_decoder_wrapper.cc

package info (click to toggle)
chromium 138.0.7204.183-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 6,071,908 kB
  • sloc: cpp: 34,937,088; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,971; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (314 lines) | stat: -rw-r--r-- 11,603 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "third_party/blink/renderer/platform/graphics/image_decoder_wrapper.h"

#include "base/system/sys_info.h"
#include "base/trace_event/trace_event.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/platform/graphics/image_decoding_store.h"
#include "third_party/blink/renderer/platform/graphics/image_frame_generator.h"

namespace blink {
namespace {

ImageDecoder::AlphaOption PixmapAlphaOption(const SkPixmap& pixmap) {
  return pixmap.alphaType() == kUnpremul_SkAlphaType
             ? ImageDecoder::kAlphaNotPremultiplied
             : ImageDecoder::kAlphaPremultiplied;
}

bool CompatibleInfo(const SkImageInfo& src, const SkImageInfo& dst) {
  if (src == dst)
    return true;

  // It is legal to write kOpaque_SkAlphaType pixels into a kPremul_SkAlphaType
  // buffer. This can happen when DeferredImageDecoder allocates an
  // kOpaque_SkAlphaType image generator based on cached frame info, while the
  // ImageFrame-allocated dest bitmap stays kPremul_SkAlphaType.
  if (src.alphaType() == kOpaque_SkAlphaType &&
      dst.alphaType() == kPremul_SkAlphaType) {
    const SkImageInfo& tmp = src.makeAlphaType(kPremul_SkAlphaType);
    return tmp == dst;
  }

  return false;
}

// Creates a SkPixelRef such that the memory for pixels is given by an external
// body. This is used to write directly to the memory given by Skia during
// decoding.
class ExternalMemoryAllocator final : public SkBitmap::Allocator {
  USING_FAST_MALLOC(ExternalMemoryAllocator);

 public:
  explicit ExternalMemoryAllocator(const SkPixmap& pixmap) : pixmap_(pixmap) {}
  ExternalMemoryAllocator(const ExternalMemoryAllocator&) = delete;
  ExternalMemoryAllocator& operator=(const ExternalMemoryAllocator&) = delete;

  bool allocPixelRef(SkBitmap* dst) override {
    const SkImageInfo& info = dst->info();
    if (kUnknown_SkColorType == info.colorType())
      return false;

    if (!CompatibleInfo(pixmap_.info(), info) ||
        pixmap_.rowBytes() != dst->rowBytes()) {
      return false;
    }

    return dst->installPixels(pixmap_);
  }

 private:
  SkPixmap pixmap_;
};

}  // namespace

ImageDecoderWrapper::ImageDecoderWrapper(
    ImageFrameGenerator* generator,
    SegmentReader* data,
    const SkPixmap& pixmap,
    ColorBehavior decoder_color_behavior,
    cc::AuxImage aux_image,
    wtf_size_t index,
    bool all_data_received,
    cc::PaintImage::GeneratorClientId client_id)
    : generator_(generator),
      data_(data),
      pixmap_(pixmap),
      decoder_color_behavior_(decoder_color_behavior),
      aux_image_(aux_image),
      frame_index_(index),
      all_data_received_(all_data_received),
      client_id_(client_id) {}

ImageDecoderWrapper::~ImageDecoderWrapper() = default;

namespace {

bool IsLowEndDeviceOrPartialLowEndModeEnabled() {
#if BUILDFLAG(IS_ANDROID)
  // Since ImageFrameGeneratorTest depends on Platform::Current(), use
  // Platform::Current()->IsLowEndDevice() here.
  return Platform::Current()->IsLowEndDevice() ||
         base::SysInfo::IsLowEndDeviceOrPartialLowEndModeEnabled();
#else
  return Platform::Current()->IsLowEndDevice();
#endif
}

}  // namespace

bool ImageDecoderWrapper::Decode(ImageDecoderFactory* factory,
                                 bool* has_alpha) {
  DCHECK(has_alpha);

  ImageDecoder* decoder = nullptr;
  std::unique_ptr<ImageDecoder> new_decoder;

  const bool resume_decoding = ImageDecodingStore::Instance().LockDecoder(
      generator_, pixmap_.dimensions(), PixmapAlphaOption(pixmap_), client_id_,
      &decoder);
  DCHECK(!resume_decoding || decoder);

  if (resume_decoding) {
    decoder->SetData(data_, all_data_received_);
  } else {
    new_decoder = CreateDecoderWithData(factory);
    if (!new_decoder)
      return false;
    decoder = new_decoder.get();
  }

  // For multi-frame image decoders, we need to know how many frames are in
  // that image in order to release the decoder when all frames are decoded.
  // `FrameCount()` is reliable only if all data is received and set in decoder,
  // particularly with GIF.
  wtf_size_t frame_count = all_data_received_ ? decoder->FrameCount() : 0u;

  const bool decode_to_external_memory =
      ShouldDecodeToExternalMemory(frame_count, resume_decoding);

  ExternalMemoryAllocator external_memory_allocator(pixmap_);
  if (decode_to_external_memory)
    decoder->SetMemoryAllocator(&external_memory_allocator);
  ImageFrame* frame = nullptr;
  {
    // This trace event is important since it is used by telemetry scripts to
    // measure the decode time.
    TRACE_EVENT0("blink,benchmark", "ImageFrameGenerator::decode");
    frame = decoder->DecodeFrameBufferAtIndex(frame_index_);
  }
  // SetMemoryAllocator() can try to access decoder's data, so we have to
  // clear it before clearing SegmentReader.
  if (decode_to_external_memory)
    decoder->SetMemoryAllocator(nullptr);
  // Verify we have the only ref-count.
  DCHECK(external_memory_allocator.unique());

  decoder->SetData(scoped_refptr<SegmentReader>(nullptr), false);
  decoder->ClearCacheExceptFrame(frame_index_);

  const bool has_decoded_frame =
      frame && frame->GetStatus() != ImageFrame::kFrameEmpty &&
      !frame->Bitmap().isNull();
  if (!has_decoded_frame) {
    decode_failed_ = decoder->Failed();
    if (resume_decoding) {
      ImageDecodingStore::Instance().UnlockDecoder(generator_, client_id_,
                                                   decoder);
    }
    return false;
  }

  SkBitmap scaled_size_bitmap = frame->Bitmap();
  DCHECK_EQ(scaled_size_bitmap.width(), pixmap_.width());
  DCHECK_EQ(scaled_size_bitmap.height(), pixmap_.height());

  // If we decoded into external memory, the bitmap should be backed by the
  // pixels passed to the allocator.
  DCHECK(!decode_to_external_memory ||
         scaled_size_bitmap.getPixels() == pixmap_.addr());

  *has_alpha = !scaled_size_bitmap.isOpaque();
  if (!decode_to_external_memory)
    scaled_size_bitmap.readPixels(pixmap_);

  // Free as much memory as possible.  For single-frame images, we can
  // just delete the decoder entirely if they use the external allocator.
  // For multi-frame images, we keep the decoder around in order to preserve
  // decoded information such as the required previous frame indexes, but if
  // we've reached the last frame we can at least delete all the cached frames.
  // (If we were to do this before reaching the last frame, any subsequent
  // requested frames which relied on the current frame would trigger extra
  // re-decoding of all frames in the dependency chain).
  const bool frame_was_completely_decoded =
      frame->GetStatus() == ImageFrame::kFrameComplete || all_data_received_;
  PurgeAllFramesIfNecessary(decoder, frame_was_completely_decoded, frame_count);

  const bool should_remove_decoder = ShouldRemoveDecoder(
      frame_was_completely_decoded, decode_to_external_memory);
  if (resume_decoding) {
    if (should_remove_decoder) {
      ImageDecodingStore::Instance().RemoveDecoder(generator_, client_id_,
                                                   decoder);
    } else {
      ImageDecodingStore::Instance().UnlockDecoder(generator_, client_id_,
                                                   decoder);
    }
  } else if (!should_remove_decoder) {
    // If we have a newly created decoder which we don't want to remove, add
    // it to the cache.
    ImageDecodingStore::Instance().InsertDecoder(generator_, client_id_,
                                                 std::move(new_decoder));
  }

  return true;
}

bool ImageDecoderWrapper::ShouldDecodeToExternalMemory(
    wtf_size_t frame_count,
    bool resume_decoding) const {
  // Some multi-frame images need their decode cached in the decoder to allow
  // future frames to reference previous frames.
  //
  // This implies extra requirements on external memory allocators for
  // multi-frame images. However, there is no enforcement of these extra
  // requirements. As a result, do not attempt to use external memory
  // allocators for multi-frame images.
  if (generator_->IsMultiFrame())
    return false;

  // On low-end devices, always use the external allocator, to avoid storing
  // duplicate copies of the data for partial decodes in the ImageDecoder's
  // cache.
  if (IsLowEndDeviceOrPartialLowEndModeEnabled()) {
    DCHECK(!resume_decoding);
    return true;
  }

  // TODO (scroggo): If !is_multi_frame_ && new_decoder && frame_count, it
  // should always be the case that 1u == frame_count. But it looks like it is
  // currently possible for frame_count to be another value.
  if (1u == frame_count && all_data_received_ && !resume_decoding) {
    // Also use external allocator in situations when all of the data has been
    // received and there is not already a partial cache in the image decoder.
    return true;
  }

  return false;
}

bool ImageDecoderWrapper::ShouldRemoveDecoder(
    bool frame_was_completely_decoded,
    bool decoded_to_external_memory) const {
  // Mult-frame images need the decode cached to allow decoding subsequent
  // frames without having to decode the complete dependency chain. For this
  // reason, we should never be decoding directly to external memory for these
  // images.
  if (generator_->IsMultiFrame()) {
    DCHECK(!decoded_to_external_memory);
    return false;
  }

  // If the decode was done directly to external memory, the decoder has no
  // data to cache. Remove it.
  if (decoded_to_external_memory)
    return true;

  // If we were caching a decoder with a partially decoded frame which has
  // now been completely decoded, we don't need to cache this decoder anymore.
  if (frame_was_completely_decoded)
    return true;

  return false;
}

void ImageDecoderWrapper::PurgeAllFramesIfNecessary(
    ImageDecoder* decoder,
    bool frame_was_completely_decoded,
    wtf_size_t frame_count) const {
  // We only purge all frames when we have decoded the last frame for a
  // multi-frame image. This is because once the last frame is decoded, the
  // animation will loop back to the first frame which does not need the last
  // frame as a dependency and therefore can be purged.
  // For single-frame images, the complete decoder is removed once it has been
  // completely decoded.
  if (!generator_->IsMultiFrame())
    return;

  // The frame was only partially decoded, we need to retain it to be able to
  // resume the decoder.
  if (!frame_was_completely_decoded)
    return;

  const wtf_size_t last_frame_index = frame_count - 1;
  if (frame_index_ == last_frame_index)
    decoder->ClearCacheExceptFrame(kNotFound);
}

std::unique_ptr<ImageDecoder> ImageDecoderWrapper::CreateDecoderWithData(
    ImageDecoderFactory* factory) const {
  if (factory) {
    auto decoder = factory->Create();
    if (decoder)
      decoder->SetData(data_, all_data_received_);
    return decoder;
  }

  const ImageDecoder::HighBitDepthDecodingOption
      high_bit_depth_decoding_option =
          pixmap_.colorType() == kRGBA_F16_SkColorType
              ? ImageDecoder::kHighBitDepthToHalfFloat
              : ImageDecoder::kDefaultBitDepth;

  // The newly created decoder just grabbed the data.  No need to reset it.
  return ImageDecoder::Create(
      data_, all_data_received_, PixmapAlphaOption(pixmap_),
      high_bit_depth_decoding_option, decoder_color_behavior_, aux_image_,
      Platform::GetMaxDecodedImageBytes(), pixmap_.dimensions());
}

}  // namespace blink