Flutter Engine
 
Loading...
Searching...
No Matches
flutter::APNGImageGenerator Class Reference

#include <image_generator_apng.h>

Inheritance diagram for flutter::APNGImageGenerator:
flutter::ImageGenerator

Public Member Functions

 ~APNGImageGenerator ()
 
const SkImageInfo & GetInfo () override
 Returns basic information about the contents of the encoded image. This information can almost always be collected by just interpreting the header of a decoded image.
 
unsigned int GetFrameCount () const override
 Get the number of frames that the encoded image stores. This method is always expected to be called before GetFrameInfo, as the underlying image decoder may interpret frame information that is then used when calling GetFrameInfo.
 
unsigned int GetPlayCount () const override
 The number of times an animated image should play through before playback stops.
 
const ImageGenerator::FrameInfo GetFrameInfo (unsigned int frame_index) override
 Get information about a single frame in the context of a multi-frame image, useful for animation and frame blending. This method should only ever be called after GetFrameCount has been called. This information is nonsensical for single-frame images.
 
SkISize GetScaledDimensions (float desired_scale) override
 Given a scale value, find the closest image size that can be used for efficiently decoding the image. If subpixel image decoding is not supported by the decoder, this method should just return the original image size.
 
bool GetPixels (const SkImageInfo &info, void *pixels, size_t row_bytes, unsigned int frame_index, std::optional< unsigned int > prior_frame) override
 Decode the image into a given buffer. This method is currently always used for sub-pixel image decoding. For full-sized still images, GetImage is always attempted first.
 
- Public Member Functions inherited from flutter::ImageGenerator
virtual ~ImageGenerator ()
 
sk_sp< SkImage > GetImage ()
 Creates an SkImage based on the current ImageInfo of this ImageGenerator.
 

Static Public Member Functions

static std::unique_ptr< ImageGeneratorMakeFromData (sk_sp< SkData > data)
 

Additional Inherited Members

- Static Public Attributes inherited from flutter::ImageGenerator
static const unsigned int kInfinitePlayCount
 Frame count value to denote infinite looping.
 

Detailed Description

Definition at line 27 of file image_generator_apng.h.

Constructor & Destructor Documentation

◆ ~APNGImageGenerator()

flutter::APNGImageGenerator::~APNGImageGenerator ( )
default

Member Function Documentation

◆ GetFrameCount()

unsigned int flutter::APNGImageGenerator::GetFrameCount ( ) const
overridevirtual

Get the number of frames that the encoded image stores. This method is always expected to be called before GetFrameInfo, as the underlying image decoder may interpret frame information that is then used when calling GetFrameInfo.

Returns
The number of frames that the encoded image stores. This will always be 1 for single-frame images.

Implements flutter::ImageGenerator.

Definition at line 43 of file image_generator_apng.cc.

43 {
44 return frame_count_;
45}

◆ GetFrameInfo()

const ImageGenerator::FrameInfo flutter::APNGImageGenerator::GetFrameInfo ( unsigned int  frame_index)
overridevirtual

Get information about a single frame in the context of a multi-frame image, useful for animation and frame blending. This method should only ever be called after GetFrameCount has been called. This information is nonsensical for single-frame images.

Parameters
[in]frame_indexThe index of the frame to get information about.
Returns
Information about the given frame. If the image is single-frame, a default result is returned.
See also
GetFrameCount

Implements flutter::ImageGenerator.

Definition at line 51 of file image_generator_apng.cc.

52 {
53 unsigned int image_index = first_frame_index_ + frame_index;
54 if (!DemuxToImageIndex(image_index)) {
55 return {};
56 }
57
58 auto frame_info = images_[image_index].frame_info;
59 if (frame_info.has_value()) {
60 return frame_info.value();
61 }
62 return {};
63}

◆ GetInfo()

const SkImageInfo & flutter::APNGImageGenerator::GetInfo ( )
overridevirtual

Returns basic information about the contents of the encoded image. This information can almost always be collected by just interpreting the header of a decoded image.

Returns
Size and color information describing the image.
Note
This method is executed on the UI thread and used for layout purposes by the framework, and so this method should not perform long synchronous tasks.

Implements flutter::ImageGenerator.

Definition at line 39 of file image_generator_apng.cc.

39 {
40 return image_info_;
41}

◆ GetPixels()

bool flutter::APNGImageGenerator::GetPixels ( const SkImageInfo &  info,
void *  pixels,
size_t  row_bytes,
unsigned int  frame_index,
std::optional< unsigned int >  prior_frame 
)
overridevirtual

Decode the image into a given buffer. This method is currently always used for sub-pixel image decoding. For full-sized still images, GetImage is always attempted first.

Parameters
[in]infoThe desired size and color info of the decoded image to be returned. The implementation of GetScaledDimensions determines which sizes are supported by the image decoder.
[in]pixelsThe location where the raw decoded image data should be written.
[in]row_bytesThe total number of bytes that should make up a single row of decoded image data (i.e. width * bytes_per_pixel).
[in]frame_indexWhich frame to decode. This is only useful for multi-frame images.
[in]prior_frameOptional frame index parameter for multi-frame images which specifies the previous frame that should be use for blending. This hints to the decoder that it should use a previously cached frame instead of decoding dependency frame(s). If an empty value is supplied, the decoder should decode any necessary frames first.
Returns
True if the image was successfully decoded.
Note
This method performs potentially long synchronous work, and so it should never be executed on the UI thread. Image decoders do not require GPU acceleration, and so threads without a GPU context may also be used.
See also
GetScaledDimensions
  1. Demux the frame from the APNG stream.
  1. Decode the frame.
  1. Composite the frame onto the canvas.

Implements flutter::ImageGenerator.

Definition at line 69 of file image_generator_apng.cc.

73 {
74 FML_DCHECK(images_.size() > 0);
75 unsigned int image_index = first_frame_index_ + frame_index;
76
77 //----------------------------------------------------------------------------
78 /// 1. Demux the frame from the APNG stream.
79 ///
80
81 if (!DemuxToImageIndex(image_index)) {
82 FML_DLOG(ERROR) << "Couldn't demux image at index " << image_index
83 << " (frame index: " << frame_index
84 << ") from APNG stream.";
85 return RenderDefaultImage(info, pixels, row_bytes);
86 }
87
88 //----------------------------------------------------------------------------
89 /// 2. Decode the frame.
90 ///
91
92 APNGImage& frame = images_[image_index];
93 SkImageInfo frame_info = frame.codec->getInfo();
94 auto frame_row_bytes = frame_info.bytesPerPixel() * frame_info.width();
95
96 if (frame.pixels.empty()) {
97 frame.pixels.resize(frame_row_bytes * frame_info.height());
98 SkCodec::Result result = frame.codec->getPixels(
99 frame.codec->getInfo(), frame.pixels.data(), frame_row_bytes);
100 if (result != SkCodec::kSuccess) {
101 FML_DLOG(ERROR) << "Failed to decode image at index " << image_index
102 << " (frame index: " << frame_index
103 << ") of APNG. SkCodec::Result: " << result;
104 return RenderDefaultImage(info, pixels, row_bytes);
105 }
106 }
107 if (!frame.frame_info.has_value()) {
108 FML_DLOG(ERROR) << "Failed to decode image at index " << image_index
109 << " (frame index: " << frame_index
110 << ") of APNG due to the frame missing data (frame_info).";
111 return false;
112 }
113 if (
114 // Check for unsigned integer wrapping for
115 // frame.{x|y}_offset + frame_info.{width|height}().
116 frame.x_offset >
117 std::numeric_limits<uint32_t>::max() - frame_info.width() ||
118 frame.y_offset >
119 std::numeric_limits<uint32_t>::max() - frame_info.height() ||
120
121 frame.x_offset + frame_info.width() >
122 static_cast<unsigned int>(info.width()) ||
123 frame.y_offset + frame_info.height() >
124 static_cast<unsigned int>(info.height())) {
125 FML_DLOG(ERROR)
126 << "Decoded image at index " << image_index
127 << " (frame index: " << frame_index
128 << ") rejected because the destination region (x: " << frame.x_offset
129 << ", y: " << frame.y_offset << ", width: " << frame_info.width()
130 << ", height: " << frame_info.height()
131 << ") is not entirely within the destination surface (width: "
132 << info.width() << ", height: " << info.height() << ").";
133 return false;
134 }
135
136 //----------------------------------------------------------------------------
137 /// 3. Composite the frame onto the canvas.
138 ///
139
140 if (info.colorType() != kN32_SkColorType) {
141 FML_DLOG(ERROR) << "Failed to composite image at index " << image_index
142 << " (frame index: " << frame_index
143 << ") of APNG due to the destination surface having an "
144 "unsupported color type.";
145 return false;
146 }
147 if (frame_info.colorType() != kN32_SkColorType) {
148 FML_DLOG(ERROR)
149 << "Failed to composite image at index " << image_index
150 << " (frame index: " << frame_index
151 << ") of APNG due to the frame having an unsupported color type.";
152 return false;
153 }
154
155 // Regardless of the byte order (RGBA vs BGRA), the blending operations are
156 // the same.
157 struct Pixel {
158 uint8_t channel[4];
159
160 uint8_t GetAlpha() { return channel[3]; }
161
162 void Premultiply() {
163 for (int i = 0; i < 3; i++) {
164 channel[i] = channel[i] * GetAlpha() / 0xFF;
165 }
166 }
167
168 void Unpremultiply() {
169 if (GetAlpha() == 0) {
170 channel[0] = channel[1] = channel[2] = 0;
171 return;
172 }
173 for (int i = 0; i < 3; i++) {
174 channel[i] = channel[i] * 0xFF / GetAlpha();
175 }
176 }
177 };
178
179 FML_DCHECK(frame_info.bytesPerPixel() == sizeof(Pixel));
180
181 bool result = true;
182
183 if (frame.frame_info->blend_mode == SkCodecAnimation::Blend::kSrc) {
184 SkPixmap src_pixmap(frame_info, frame.pixels.data(), frame_row_bytes);
185 uint8_t* dst_pixels = static_cast<uint8_t*>(pixels) +
186 frame.y_offset * row_bytes +
187 frame.x_offset * frame_info.bytesPerPixel();
188 result = src_pixmap.readPixels(info, dst_pixels, row_bytes);
189 if (!result) {
190 FML_DLOG(ERROR) << "Failed to copy pixels at index " << image_index
191 << " (frame index: " << frame_index << ") of APNG.";
192 }
193 } else if (frame.frame_info->blend_mode ==
194 SkCodecAnimation::Blend::kSrcOver) {
195 for (int y = 0; y < frame_info.height(); y++) {
196 auto src_row = frame.pixels.data() + y * frame_row_bytes;
197 auto dst_row = static_cast<uint8_t*>(pixels) +
198 (y + frame.y_offset) * row_bytes +
199 frame.x_offset * frame_info.bytesPerPixel();
200
201 for (int x = 0; x < frame_info.width(); x++) {
202 auto x_offset_bytes = x * frame_info.bytesPerPixel();
203
204 Pixel src = *reinterpret_cast<Pixel*>(src_row + x_offset_bytes);
205 Pixel* dst_p = reinterpret_cast<Pixel*>(dst_row + x_offset_bytes);
206 Pixel dst = *dst_p;
207
208 // Ensure both colors are premultiplied for the blending operation.
209 if (info.alphaType() == kUnpremul_SkAlphaType) {
210 dst.Premultiply();
211 }
212 if (frame_info.alphaType() == kUnpremul_SkAlphaType) {
213 src.Premultiply();
214 }
215
216 for (int i = 0; i < 4; i++) {
217 dst.channel[i] =
218 src.channel[i] + dst.channel[i] * (0xFF - src.GetAlpha()) / 0xFF;
219 }
220
221 // The final color is premultiplied. Unpremultiply to match the
222 // backdrop surface if necessary.
223 if (info.alphaType() == kUnpremul_SkAlphaType) {
224 dst.Unpremultiply();
225 }
226
227 *dst_p = dst;
228 }
229 }
230 }
231
232 return result;
233}
int32_t x
const gchar * channel
#define FML_DLOG(severity)
Definition logging.h:121
#define FML_DCHECK(condition)
Definition logging.h:122
double y

References channel, FML_DCHECK, FML_DLOG, i, x, and y.

◆ GetPlayCount()

unsigned int flutter::APNGImageGenerator::GetPlayCount ( ) const
overridevirtual

The number of times an animated image should play through before playback stops.

Returns
If this image is animated, the number of times the animation should play through is returned, otherwise it'll just return 1. If the animation should loop forever, kInfinitePlayCount is returned.

Implements flutter::ImageGenerator.

Definition at line 47 of file image_generator_apng.cc.

47 {
48 return frame_count_ > 1 ? play_count_ : 1;
49}

◆ GetScaledDimensions()

SkISize flutter::APNGImageGenerator::GetScaledDimensions ( float  scale)
overridevirtual

Given a scale value, find the closest image size that can be used for efficiently decoding the image. If subpixel image decoding is not supported by the decoder, this method should just return the original image size.

Parameters
[in]scaleThe desired scale factor of the image for decoding.
Returns
The closest image size that can be used for efficiently decoding the image.
Note
This method is called prior to GetPixels in order to query for supported sizes.
See also
GetPixels

Implements flutter::ImageGenerator.

Definition at line 65 of file image_generator_apng.cc.

65 {
66 return image_info_.dimensions();
67}

◆ MakeFromData()

std::unique_ptr< ImageGenerator > flutter::APNGImageGenerator::MakeFromData ( sk_sp< SkData >  data)
static

Definition at line 235 of file image_generator_apng.cc.

236 {
237 // Ensure the buffer is large enough to at least contain the PNG signature
238 // and a chunk header.
239 if (data->size() < sizeof(kPngSignature) + sizeof(ChunkHeader)) {
240 return nullptr;
241 }
242 // Validate the full PNG signature.
243 const uint8_t* data_p = static_cast<const uint8_t*>(data.get()->data());
244 if (memcmp(data_p, kPngSignature, sizeof(kPngSignature))) {
245 return nullptr;
246 }
247
248 // Validate the header chunk.
249 const ChunkHeader* chunk = reinterpret_cast<const ChunkHeader*>(data_p + 8);
250 if (!IsValidChunkHeader(data_p, data->size(), chunk) ||
251 chunk->get_data_length() != sizeof(ImageHeaderChunkData) ||
252 chunk->get_type() != kImageHeaderChunkType) {
253 return nullptr;
254 }
255
256 // Walk the chunks to find the "animation control" chunk. If an "image data"
257 // chunk is found first, this PNG is not animated.
258 while (true) {
259 chunk = GetNextChunk(data_p, data->size(), chunk);
260
261 if (chunk == nullptr) {
262 return nullptr;
263 }
264 if (chunk->get_type() == kImageDataChunkType) {
265 return nullptr;
266 }
267 if (chunk->get_type() == kAnimationControlChunkType) {
268 break;
269 }
270 }
271
272 const AnimationControlChunkData* animation_data =
273 CastChunkData<AnimationControlChunkData>(chunk);
274
275 // Extract the header signature and chunks to prepend when demuxing images.
276 std::optional<std::vector<uint8_t>> header;
277 const void* first_chunk_p;
278 std::tie(header, first_chunk_p) = ExtractHeader(data_p, data->size());
279 if (!header.has_value()) {
280 return nullptr;
281 }
282
283 // Demux the first image in the APNG chunk stream in order to interpret
284 // extent and blending info immediately.
285 std::optional<APNGImage> default_image;
286 const void* next_chunk_p;
287 std::tie(default_image, next_chunk_p) =
288 DemuxNextImage(data_p, data->size(), header.value(), first_chunk_p);
289 if (!default_image.has_value()) {
290 return nullptr;
291 }
292
293 unsigned int play_count = animation_data->get_num_plays();
294 if (play_count == 0) {
295 play_count = kInfinitePlayCount;
296 }
297
298 SkImageInfo image_info = default_image.value().codec->getInfo();
299 return std::unique_ptr<APNGImageGenerator>(
300 new APNGImageGenerator(data, image_info, std::move(default_image.value()),
301 animation_data->get_num_frames(), play_count,
302 next_chunk_p, header.value()));
303}
static const unsigned int kInfinitePlayCount
Frame count value to denote infinite looping.
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot data
Definition switch_defs.h:36

References data.

Referenced by flutter::ImageGeneratorRegistry::ImageGeneratorRegistry().


The documentation for this class was generated from the following files: