-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.html
254 lines (254 loc) · 21.7 KB
/
index.html
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
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="[MAZ01001.github.io] GIF decoding and rendering with native JavaScript and HTML5 canvas">
<meta name="author" content="MAZ01001">
<link rel="apple-touch-icon" sizes="180x180" href="../img/apple-touch-icon.png">
<link rel="icon" type="image/x-icon" href="../img/MAZ_logo.svg">
<link rel="icon" type="image/png" sizes="32x32" href="../img/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="../img/favicon-16x16.png">
<link rel="manifest" href="../img/site.webmanifest">
<link rel="mask-icon" href="../img/safari-pinned-tab.svg" color="#ff9900">
<link rel="shortcut icon" href="../img/favicon.ico">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="msapplication-config" content="../img/browserconfig.xml">
<meta name="theme-color" content="#ffffff">
<title>GIF decoder</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<noscript>This page needs JavaScript to work</noscript>
<div id="box">
<h1 id="gifHeading">GIF<span class="hint" title="Only supports GIF files with version "GIF89a"">*</span> decoder</h1>
<!-- TODO
! add toggles to URL parameters & as button left of playback controls
- >> ? when toggling any of these → redraw GIF ?!
- >> (ON) for clear at [0] (ignores last disposal nmethod) ~ seeks from [0] to frameIndex if frameIndex < frameIndexLast ~ also more performant for reverse play
- >> (ON) for forcing background color to be transparent when restoring to background color
- >> (OFF) for true inverse render ~ when reverse play render from frameIndexLast to frameIndex ~ disposal methods are also in reverse ~ probably faster but visually unexpected
- >> (OFF) for no drawing text extension
! new canvas button to change canvas background/-color
- >> dialog menu with some options and a live preview
- >> toggle to use backgroud color index (disabled in OFF state if not available and disables all following inputs if toggled ON)
- >> >> color input for background color
- >> >> checkbox to toggle checker texture and enable checker color (second color input)
- >> >> presets white/bright/dark/black/transparent/custom
- >> >> >> checker-a, -b, and -len (see CSS#L176)
! implement pixel aspect ratio via CSS transforms
! implement failsaves when loading/reading corrupted/incomplete files
? auto hide gifView buttons after 1sec no mouse movement (js mouse move) or mouse leaves div (css hover)
? useless/verbose stats
? (AppExt) raw bytes as hex string (textarea) ~ instead of copy button ? both
? add [byte position] to title ~ comment, application/unkown extension, and global/local color table
? create example GIF file with all known features - https://www.w3.org/Graphics/GIF/spec-gif89a.txt - via GIMP and then edit in HEX-editor ~ create empty file with new size then override all bytes (since vscode-hexeditor can't add bytes...yet)
- >> - 500*500 px total
- >> - 30 frames
- >> - irregular frame delays
- >> - not full frame changes (frames are smaller than whole GIF)
- >> - every disposal method
- >> - ! requires some planning in the frame image contents ~ visually
- >> - loops 3 times
- >> - global color table and at least one local color table (can be a copy of global color table)
- >> - use transparency of local color table
- >> - user input flag on at least two frames (with and without timeout)
- >> - unknown app extension
- >> - 0x21FF0C544553545F4150502045585400
- >> - text extensions for first 10 frames (graphics control extension > image descriptor > [text extension] > image data)
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B305D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B315D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B325D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B335D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B345D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B355D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B365D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B375D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B385D00
- >> - 0x21010C0A000A00200010000408 <forground_global_color_index_1B> <background_global_color_index_1B> 105445585420666F726672616D655B395D00
- >> - comment after image descriptor and before image data for the last frame
- >> - 0x21FE4D5445535420636F6D6D656E740D0A616674657220696D6167652064657363726970746F7220616E64206265666F726520696D616765206461746120666F7220746865206C617374206672616D6500
- >> - comment after image descriptor and before image data for the 10th frame
- >> - 0x21FE4D5445535420636F6D6D656E740D0A616674657220696D6167652064657363726970746F7220616E64206265666F726520696D616765206461746120666F72207468652031307468206672616D6500
- >> - comment after image descriptor and before image data for the 100th frame
- >> - 0x21FE4E5445535420636F6D6D656E740D0A616674657220696D6167652064657363726970746F7220616E64206265666F726520696D616765206461746120666F7220746865203130307468206672616D6500
- >> - unknown extensions (after image data (and app extension) of frames 0-3)
- >> - unknown extension 0
- >> - 0x2154115445535420657874656E73696F6E20233000
- >> - unknown extension 1
- >> - 0x2145115445535420657874656E73696F6E20233100
- >> - unknown extension 2
- >> - 0x2153115445535420657874656E73696F6E20233200
- >> - unknown extension 3
- >> - 0x2154115445535420657874656E73696F6E20233300
? make calculating frame index/time and rendering into functions for the GIFdecodeModule.js and the GIF decode section at the beginning of this file
- >> seekGIFplayback(GIF,loopIndex,loopLength,currentFrameIndex,currentTime,seekDeltaTime,skipUserInput?)=>[newLoopIndex,newFrameIndex,newTime,paused]
- >> renderGIFplayback(GIF,currentFrameIndex,newFrameIndex,options:{viewCanvas,frameCanvas,storeCanvas,forceClearStart,forceTransparentBackground,reverseRender,???})=>??? ~ maybe generator function
-->
<div id="gifView" title="GIF render canvas">
<canvas id="htmlCanvas">Your browser does not support the HTML canvas element</canvas>
<div id="gifViewButtons">
<input type="button" id="fullWindow" data-toggle="0" title="Toggle canvas fullscreen window" value="⤢">
<input type="button" id="fitWindow" data-toggle="0" title="Toggle between fit to window (🞕) and actual size (🞑)" value="🞕">
<input type="button" id="imgSmoothing" data-toggle="0" title="Toggle between pixelated (🙾) and smooth (🙼) image rendering" value="🙾">
</div>
<span id="gifFPS" title="Shows FPS for GIF canvas (not related to GIF speed)">fps -</span>
<div id="gifLoad" title="Shows current decoding progress">
<p id="gifLoadText">Loading...</p>
<progress id="gifLoadProgress"></progress>
</div>
</div>
<label for="timeRange" title="Current timestamp (with frame timestamps in tickmarks below)">Time: <input type="range" id="timeRange" list="timeTickmarks" aria-disabled="true" tabindex="-1"> <span id="time">-</span> ms</label>
<datalist id="timeTickmarks"></datalist>
<label for="frameRange" title="Current frame index (zero-based)">Frame: <input type="range" id="frameRange" step="1"> <span id="frame">-</span></label>
<div id="overrideOptions">
<input type="button" id="overrideMenu" data-toggle="0" value="Override">
<div>
<p>Override default render behaviour <small>(WIP)</small></p>
<label for="overrideClear" title="Allways clears the canvas between the last and the first frame - ignoring the disposal method of the last frame"><input type="checkbox" id="overrideClear"> Clear canvas on frame 0 <small>(slightly improves the performance impact of reverse playback)</small></label>
<label for="overrideBackground" title="Allways uses transparent when restoring to backgrond color (sets drawn pixels transparent) - ignoring the background color and the transparency color of the current frame"><input type="checkbox" id="overrideBackground"> Restoring to background color is allways transparent</label>
<label for="overrideText" title="Ignores the text extension of the current frame"><input type="checkbox" id="overrideText"> Don't render text extension</label>
<label for="overrideRender" title="Renders frames in reverse order (to the left on the timeline instead of to the right) - may look very weird"><input type="checkbox" id="overrideRender"> Inverse render order <small>(just for fun - swaps performance impact for forward/reverse playback)</small></label>
</div>
</div>
<div id="playerControls" class="paused">
<input type="button" id="seekStart" value="⏮" title="first frame (paused)">
<input type="button" id="seekPrevious" value="⇤" title="previous frame (paused)">
<input type="button" id="reverse" value="⏴" title="reverse play">
<input type="button" id="pause" value="⏸" title="pause">
<input type="button" id="play" value="⏵" title="play">
<input type="button" id="seekNext" value="⇥" title="next frame (paused)">
<input type="button" id="seekEnd" value="⏭" title="last frame (paused)">
</div>
<input type="number" id="speed" value="1" min="0" max="100" step="any" placeholder="1" title="playback speed multiplier [0 to 100] (use arrow keys with shift/ctrl/alt for more precision)">
<fieldset id="userInputArea"><legend title="If highlighted blue needs user input to continue playback">User input</legend>
<label for="userInputTimeout" title="Timeout for current frame (if present / when waiting for user input)">Timeout: <progress id="userInputTimeout" value></progress> <span id="userInputTimeoutTime">-</span> ms</label>
<input type="button" id="userInput" title="Click to continue playing GIF" value="Continue">
<input type="button" id="userInputLock" data-toggle="0" title="Click to toggle instant user input" value="Lock">
</fieldset>
<fieldset id="loopArea"><legend title="Control GIF looping behaviour">Loop override</legend>
<span title="Shows current and maximum GIF loop amount"><span id="loopText">-</span> Loops</span>
<input type="button" id="loopForce" data-toggle="0" title="Click to (toggle) force infinite repeats" value="Force infinite">
</fieldset>
<div id="infoPanels">
<div>
<input type="button" id="open" value="Open GIF" title="Click to open the import menu">
<a target="_blank" rel="noopener noreferrer" href="https://github.com/MAZ01001/GIF_decoder" title="GitHub project page with documentation">GitHub project / Documentation</a>
</div>
<fieldset><legend title="General information about the GIF">GIF info</legend>
<details open id="detailsGIFInfo"><summary>Click to toggle contents</summary>
<div id="gifInfoArea">
<table>
<tr><th>Name</th><th>Value</th></tr>
<tr title="The filename of the GIF currently loaded"><td>Filename</td><td id="fileName">____</td></tr>
<tr title="The width*height of the entire GIF"><td>Size</td><td><span id="totalWidth">-</span> * <span id="totalHeight">-</span> px</td></tr>
<tr title="The amount of frames in this GIF"><td>Total frames</td><td id="totalFrames">-</td></tr>
<tr title="The (maximum) total time of the entire GIF"><td>Total Time</td><td id="totalTime">-</td></tr>
<tr title="The Pixel aspect ratio of the GIF"><td>Pixel aspect ratio</td><td id="pixelAspectRatio">-:-</td></tr>
<tr title="The color resolution of the GIF"><td>Color resolution</td><td id="colorRes">-</td></tr>
<tr title="The index of the color used for the background (if available)"><td>Background color</td><td id="backgroundColorIndex">-</td></tr>
</table>
<fieldset><legend title="The global color table of the GIF">Global color table</legend>
<details open id="detailsGlobalColorTable"><summary>Click to toggle contents</summary>
<div id="globalColorTable"><span>Loading...</span></div>
</details>
</fieldset>
<fieldset><legend title="Application-Extensions in the GIF file">Application-Extensions</legend>
<details open id="detailsAppExtList"><summary>Click to toggle contents</summary>
<div id="appExtList"><span>Loading...</span></div>
</details>
</fieldset>
<fieldset><legend title="Comments in the GIF file and the frame index of where they were found">Comments</legend>
<details id="detailsCommentsList"><summary>Click to toggle contents</summary>
<div id="commentsList"><span>Loading...</span></div>
</details>
</fieldset>
<fieldset><legend title="Unknown extensions in the GIF file">Unknown extensions</legend>
<details id="detailsUnExtList"><summary>Click to toggle contents</summary>
<div id="unExtList"><span>Loading...</span></div>
</details>
</fieldset>
</div>
</details>
</fieldset>
<fieldset><legend title="Information of the currently displayed frame">Frame info</legend>
<details id="detailsFrameView"><summary>Click to toggle frame canvas</summary>
<div id="frameView" title="Frame render canvas">
<canvas id="htmlFrameCanvas">Your browser does not support the HTML canvas element</canvas>
<div id="frameViewButtons">
<input type="button" id="frameFullWindow" data-toggle="0" title="Toggle canvas fullscreen window" value="⤢">
<input type="button" id="frameFitWindow" data-toggle="0" title="Toggle between fit to window (🞕) and actual size (🞑)" value="🞕">
<input type="button" id="frameImgSmoothing" data-toggle="0" title="Toggle between pixelated (🙾) and smooth (🙼) image rendering" value="🙾">
</div>
<span id="frameFPS" title="Shows FPS for frame canvas (not related to GIF speed)">fps -</span>
<div id="frameLoad" title="Shows current decoding progress">
<p id="frameLoadText">Loading...</p>
<progress id="frameLoadProgress"></progress>
</div>
</div>
</details>
<details id="detailsFrameInfo"><summary>Click to toggle contents</summary>
<div id="frameInfoArea">
<table>
<tr><th>Name</th><th>Value</th></tr>
<tr title="The width*height of the current frame"><td>Size</td><td><span id="frameWidth">-</span> * <span id="frameHeight">-</span> px</td></tr>
<tr title="The position of the current frame from the left/top edge of the GIF"><td>Position</td><td><span id="frameLeft">-</span> / <span id="frameTop">-</span> px</td></tr>
<tr title="How long this frame is displayed for"><td>Time</td><td id="frameTime">- ms</td></tr>
<tr title="If the user input flag is set for the current frame"><td>User input flag</td><td><input type="checkbox" id="frameUserInputFlag" disabled></td></tr>
<tr title="The index of the color that is used for transparency (if available)"><td>Transparent color</td><td id="transparentColorIndex">-</td></tr>
<tr title="The disposal method of the current frame"><td>Disposal method</td><td id="frameDisposalMethod">[- ____]<br>________</td></tr>
<tr title="2bits reserved for future use (from a packed field in the image descriptor block)"><td>Reserved field (ID)</td><td id="frameReserved">- (0b00)</td></tr>
<tr title="3bits reserved for future use (from a packed field in the graphics control extension block)"><td>Reserved field (GC)</td><td id="frameGCReserved">- (0b000)</td></tr>
</table>
<fieldset><legend>Local color table</legend>
<details open id="detailsFrameColorTable"><summary>Click to toggle contents</summary>
<div id="frameColorTable"><span>Loading...</span></div>
</details>
</fieldset>
<fieldset><legend title="Text that is displayed during this frame">Text</legend>
<details id="detailsFrameTextInfo"><summary>Click to toggle contents</summary>
<div id="frameTextArea">
<table>
<tr><th>Name</th><th>Value</th></tr>
<tr title="The text to display on the current frame"><td>String</td><td id="frameText">____</td></tr>
<tr title="The width*height of the character grid"><td>Grid size</td><td><span id="frameTextWidth">-</span> * <span id="frameTextHeight">-</span> px</td></tr>
<tr title="The position of the character grid from the left/top edge of the GIF"><td>Grid position</td><td><span id="frameTextLeft">-</span> / <span id="frameTextTop">-</span> px</td></tr>
<tr title="The width*height for each cell in the character grid"><td>Character size</td><td><span id="frameTextCharWidth">-</span> * <span id="frameTextCharHeight">-</span> px</td></tr>
<tr title="The foreground color of the text (click to copy hex code)"><td>Foreground</td><td><input type="color" id="frameTextCharForeground"></td></tr>
<tr title="Teh background color of the text (click to copy hex code)"><td>Background</td><td><input type="color" id="frameTextCharBackground"></td></tr>
</table>
</div>
</details>
</fieldset>
</div>
</details>
</fieldset>
</div>
</div>
<dialog id="importMenu">
<h2>Import</h2>
<fieldset><legend title="Load a GIF from URL or a file">Load a GIF file</legend>
<label for="importURL" title="Load GIF from URL">URL: <input type="url" id="importURL" placeholder="https://example.com/test.gif"></label>
<p>-- OR --</p>
<label for="importFile" title="Load local GIF file">File: <input type="file" id="importFile" accept=".gif"></label>
</fieldset>
<p id="importWarn"></p>
<fieldset><legend title="Preview of selected GIF">GIF preview</legend>
<img id="importPreview" alt="GIF img preview">
</fieldset>
<div id="importActions">
<input type="button" id="importConfirm" disabled value="Start" title="Start decoding GIF">
<input type="button" id="importAbort" autofocus value="Abort" title="Close import menu">
</div>
</dialog>
<dialog id="confirmMenu">
<h2>Are you sure?</h2>
<span id="confirmText">________</span>
<div>
<input type="button" id="confirmConfirm" value="OK" title="confirm action">
<input type="button" id="confirmAbort" value="Abort" title="abort action" autofocus>
</div>
</dialog>
<script src="./script.js"></script>
</body>
</html>