mirror of
https://github.com/SerenityOS/serenity
synced 2026-05-14 19:06:55 +02:00
AnimationWriter already only stores the smallest rect that contains changing pixels between two frames. For example, when doing a screen recording and only the mouse cursor moves, we already only encode the pixels in the (single) rectangle containing old and new mouse cursor positions. Within that rectangle, there can still be many pixels that are identical over the two frames. When possible, we now replace all identical pixels with transparent black. This has two advantages: 1. It can reduce the number of colors in the image. In particular, for wow.gif (and likely many other gifs), new frames had more than 256 colors before, and have fewer than 256 colors after this change. 2. Long run of identical pixels compress better. In some cases, this transform might make things slighly worse, for example if the input image already consists of long runs of a single color. We'll now add another color to it (transparent black), without it helping much. And the decoder now must do some blending, slowing down decoding a bit. But most of the time this should be a pretty big win. We can tweak the heuristic when to do it later. This transform is possible when: * The new frame doesn't already have transparent pixels (which are different from the old frame) * The encoder/decoder can handle frames with transparent pixels For the latter reason, encoders currently have to opt in to this.
50 lines
1.7 KiB
C++
50 lines
1.7 KiB
C++
/*
|
|
* Copyright (c) 2024, Nico Weber <thakis@chromium.org>
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <AK/Error.h>
|
|
#include <LibGfx/Forward.h>
|
|
#include <LibGfx/Point.h>
|
|
|
|
namespace Gfx {
|
|
|
|
class AnimationWriter {
|
|
public:
|
|
virtual ~AnimationWriter();
|
|
|
|
enum class BlendMode {
|
|
// The new frame replaces the data below it.
|
|
Replace,
|
|
|
|
// The new frame is blended on top of the data below it.
|
|
// Use only when the new frame has completely opaque and completely transparent pixels.
|
|
// The opaque pixels will replace the pixels below them, the transparent pixels will leave pixels below them unchanged.
|
|
// Use only with AnimationWriter subclasses that return true from can_blend_frames().
|
|
Blend,
|
|
};
|
|
|
|
// Flushes the frame to disk.
|
|
// IntRect { at, at + bitmap.size() } must fit in the dimensions
|
|
// passed to `start_writing_animation()`.
|
|
virtual ErrorOr<void> add_frame(Bitmap&, int duration_ms, IntPoint at = {}, BlendMode disposal_method = BlendMode::Replace) = 0;
|
|
|
|
// If this is set to Yes and can_blend_frames() returns true, add_frame_relative_to_last_frame() may
|
|
// call add_frame() with BlendMode::Blend and a frame that has transparent pixels.
|
|
enum class AllowInterFrameCompression {
|
|
No,
|
|
Yes,
|
|
};
|
|
ErrorOr<void> add_frame_relative_to_last_frame(Bitmap&, int duration_ms, RefPtr<Bitmap> last_frame, AllowInterFrameCompression = AllowInterFrameCompression::Yes);
|
|
|
|
virtual bool can_blend_frames() const { return false; }
|
|
|
|
private:
|
|
bool can_zero_out_unchanging_pixels(Bitmap& new_frame, Gfx::IntRect new_frame_rect, Bitmap& last_frame, AllowInterFrameCompression) const;
|
|
};
|
|
|
|
}
|