main() capped resample() at the square-cell frame budget (128 across 4
columns) regardless of cell shape, but layout() forces a wide/tall cell
to a single column, whose real budget is only 32. A long animation in
a non-square cell would blow past MAX_TEXTURE and die instead of
resampling down to a fitting framerate.
ClassicAPI's texture dimension gate lifts the two limits the sheet layout was
built around. A sheet skinnier than 16:1 drawing nothing was never a client
rule -- VanillaHelpers grew the texture recycle pool to 6x6 but left the index
stride at 5, so a 32x1024 strip collided with a 64x32 bucket and was handed
back the wrong texture. And power-of-two is no longer required, since the gate
grows the decode scratch to fit.
So a run wraps into columns only when it outgrows the scratch at 32 frames, and
a sheet is sized to its frames exactly: 20 frames is a 32x640 strip rather than
64x1024. The 128 frame budget is unchanged, and older sheets still play -- the
animator reads the column count off the sheet.
A GIF frame delay under 20ms means "as fast as possible", and browsers clamp
it to 100ms - the speed the emote actually plays at where people see it.
Summed literally, :Awkward: reported 76 frames lasting 0.1s and collapsed to
a single still. Any emote authored that way would have silently become a
static image.
A source wider than it is tall was squeezed into a square 32x32 cell, so it
rendered as a letterboxed sliver in a 28x28 box. The frame cell now follows
the source - 32 tall, as wide as its aspect wants - and the payload carries
a matching display size. Cinema (298x112) goes from a 32x12 strip with
transparent bands to an 85x32 cell filling a 28x74 box.
Notes on the payload: it reads HEIGHT first then width, per ParseIcon in
ClassicAPI's InlineTexture.cpp. A square still frame keeps its bare :28:28
with no crop; anything else needs one to pick its cell out of the texture.
Non-square cells stay single-column, since the animator derives its column
count as imageWidth / frameWidth, which only holds when the cell tiles the
texture width exactly. layout() also checks the 16:1 limit both ways now - a
very wide sheet can bust it as easily as a tall one.
--file decouples the texture basename from the trigger word, so a :token:
emote is possible at all: ':Cinema:.tga' is not a legal filename.
A texture skinnier than 16:1 draws nothing at all. Confirmed in-game with
one crop shape across two sheets: ratJAM (32x512) renders, modCheck
(32x1024) is blank. The earlier 2048 repack fixed those sheets by accident
- two columns took them from 64:1 to 16:1, not by getting under the 1024
cap as its commit message claimed.
Twenty-seven sheets were still at 32:1 and had never rendered: modCheck,
meow, BOOBA, rooRave, Aware, Thinkge, alert and the rest. All repacked to
64x512, same frames in the same order, with their crops and metadata
updated. Verified across all 112 animated sheets: none over 1024 or
skinnier than 16:1, declared sizes match the files, frame counts fit their
grids, and every repacked frame still matches what its sheet held at that
index.
add_emote.py learns the same limit, and resamples the source timeline at a
constant rate instead of expanding holds into repeats: a hold repeats, a
source faster than the ~30fps ticker drops frames, and the loop keeps its
duration either way. blobDance is 50fps at source, so the old code stretched
its 0.74s loop to 1.23s.
Also drops the atoi note from BuildFrameString - ParseField uses atof and
tolerates decimals, so that was never the failure it described.
tools/add_emote.py downloads a BetterTTV emote, converts it to a texture
the 1.12 client decodes, and registers it in defaultpack, emoticons, the
animation table and the minimap pack list.
Two client limits shape the conversion. Textures cap at 1024px per side,
so anything longer than 32 frames packs row-major across several 32px
columns. And the animator plays one constant rate while a GIF holds
individual frames for different lengths, so uneven delays expand into
repeated frames at the integer rate that reproduces the source timing with
the fewest frames - a faster rate always matches the duration more exactly
but pays for it in texture size.
Nerdge is the first emote through it, replacing the static art with the
animated original: 17 source frames over 3.12s become 36 frames at 11fps
(3.27s) in a 64x1024 sheet, keeping its hold, push, hold shape.
.pkgmeta is tracked now so the release packager honours its ignore list
and leaves tools out of the zip.