Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

The treatment of extensions #79

Open
HeroicKatora opened this issue Sep 19, 2020 · 1 comment
Open

The treatment of extensions #79

HeroicKatora opened this issue Sep 19, 2020 · 1 comment

Comments

@HeroicKatora
Copy link
Member

A draft of how to treat extensions. The main challenge is that the decoder assumes to be at particular states when calling next_frame_info, read_next_frame and read_into_buffer. Futhermore, the descriptor is returned as a struct but the image data is written into a buffer whose size must obey the size given by the descriptor. This is motivated by having some constraints on the order of chunks as seen in the spec:

The Grammar.

<GIF Data Stream> ::=     Header <Logical Screen> <Data>* Trailer

<Logical Screen> ::=      Logical Screen Descriptor [Global Color Table]

<Data> ::=                <Graphic Block>  |
                          <Special-Purpose Block>

<Graphic Block> ::=       [Graphic Control Extension] <Graphic-Rendering Block>

<Graphic-Rendering Block> ::=  <Table-Based Image>  |
                               Plain Text Extension
[note: these must be consecutive]
<Table-Based Image> ::=   Image Descriptor [Local Color Table] Image Data

<Special-Purpose Block> ::=    Application Extension  |
                               Comment Extension

There must be some amount of code flow to the caller between returning the desciptor and writing the data. And considering Rust's borrowing system it must allow the caller to prepare a mutable reference to a buffer. That is, it must not be borrow by the decoder when the transfer takes place.

Buffer extensions

Currently the interface of StreamingDecoder, but not really implemented or supported, has an option to store extensions and retrieve them later. This requires allocating some additional memory but any other interface is unchanged. We might make this a bit more precise by allowing specifying which extension to save, or a list of application identifiers. This does not require any change in control flow.

An iterator style

Implemented by gift. An iterator yields each block individually. This needs to be reconciled with the high-level interface that reads images into a buffer. We also need to either a) ensure that the input buffer contains a block fully or b) expose the partial block in the iterator or c) allocate all blocks as gift does. This essentially transfers control to the caller after every single sub-block or block.

The awkward thing is that we can not both borrow the target buffer and have the iterator be 'static. This effectively means we need to allocate image data as well.

The cursor style

Instead of yielding an independent item on iteration, produce items that reference into the decoder. This wouldn't be able to implement Iterator hence I call it cursor for now. The advantage is that we need not clone and allocate all items. Also all color tables and graphic blocks might be handled internally. It's somewhat questionable how this relates to the code flow transfer though. As said before we must yield to the caller after the desciptor.

Basically, the next method must somehow temporarily borrow any buffer it wants to write to. This currently refers to the image buffer so that we can effectively decode interlacing and maps. But what if we want to internally validate comments? Or the plain text extension? Both should contain ascii data, and it's mandatory for the latter.

The callback style

Store a Weak<dyn CallbackTrait + Send + Sync + 'static> and invoke those callbacks on fitting blocks. We need weak to ensure that there are no unwanted cycles, and the additional marker bounds to keep the current Send and Sync marker impls on the main decoder interface. We might combine this with the adaptor interface in giving the option for a temporary non-send or non-sync callback. The immediate drawback is that this requires an allocation.

@HeroicKatora
Copy link
Member Author

The cursor style is the only for which I couldn't come up with any immediate drawbacks. It's also mostly compatible with existing interfaces as they could call it with or without an image buffer, and discard any unexpected result. That's what they currently do internally.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant