comic_motion 1.3.2
comic_motion: ^1.3.2 copied to clipboard
Pure-Dart engine that splits a static comic page into depth layers and renders 32 layered comic effects as deterministic, byte-reproducible GIF/APNG output.
Changelog #
All notable changes to this project are documented in this file. Versioning
follows SemVer. pubspec.yaml, lib/src/version.dart
and dart run bin/comic_motion.dart --version share one version constant.
The full changelog in Chinese — including all historical entries — lives in doc/CHANGELOG_zh-CN.md.
1.3.2 - 2026-09-29 #
Publishing and documentation polish; no behavior changes. The byte-reproducibility contract is untouched (253 core + 6 server tests green).
- Runnable example (
example/example.dart): renders a short layered-effect sequence end to end (MotionPipelinewithonProgress+processFile), so the package now ships a standardexample/entry point. - Docs: package description shortened; CHANGELOG fully in English (the
historical Chinese changelog moved to
doc/CHANGELOG_zh-CN.md). - Lint: snake_case local names in
src/render/resampler.dartrenamed to lowerCamelCase; stale dartdoc references cleaned up.
1.3.1 - 2026-09-28 #
Release-oriented rework + runtime lifecycle + correctness protection + high-fps and ecosystem, published together. The byte-reproducibility contract held throughout (legacy pixel path and encoder bytes untouched; test guard grew to 253 core + 6 server cases, all green).
High-fps & ecosystem #
- Roadmap refresh (
doc/roadmap.md): implemented items removed; remaining = GPU particle effects (shader companion, phase 2), AI model depth (kept behind theDepthEstimatorinjection route), animated WebP fallback, multi-axis interaction frame sets (new). - Injectable AI depth (architecture):
DepthEstimatoris now an abstract interface (estimate(RgbaImage) → DepthMap); the built-in heuristic implementation was renamedHeuristicDepthEstimator(breaking rename, pixel output byte-identical, test-locked).MotionPipeline(depthEstimator:)accepts external ML estimators (e.g. tflite on the app side) while the core stays dependency-free. NewexportLayers/exportLayersFile(lib/src/layer_export.dart): layered raster export to PNG texture set +index.json(kind: layers), pixel-identical to the pipeline's internal layering; output dir<stem>_<contentHash8>_<configHash8>_layers/. Newdoc/external-depth.mdandtest/depth_interface_test.dart. - Panel-aware layering (MVP, horizontal panels only): new
PanelSplitter(horizontal whitespace scanning; ≥2 panels required, falls back to full-page otherwise) andEffectConfig.panelAware(opt-in, conditionally serialized — default false keeps the fingerprint unchanged). Per-panel independent depth/layering with cross-panel bleed eliminated (test-locked), panel-relative parallax normalization, deterministic in both parallel and serial runs, orthogonal to strip mode. Newtest/panel_aware_test.dart. - High-fps presets:
encoding.apngDelay: 'exact'(opt-in) writes exact fcTL fractional delays (60fps = 16.67ms; the defaultcsvariant stays byte-identical to 1.3). APNG rect inter-frame diffing (diffMode: rectextended to APNG): worker returns full-frame RGBA, the main isolate diffs change rects → area frames (dispose=NONE/blend=SOURCE; 1x1 placeholder keeps timing), O(single frame) memory. Newtool/bench_high_fps.dart(rect saves 41–57% for local motion; zero gain for full-frame motion) anddoc/high-fps.md.estimateCostnow covers APNG/rect. - Deterministic preview assets:
tool/generate_showcase.dart --previews-onlyproduces 40 low-res previews (8fps, 1.5s, ≤360px, each GIF < 300KB) underdoc/previews/<demo>/with a deterministicindex.json(two runs are byte-identical, sha1-locked);kEffectPreviewRefsmaps all 32 effects to previews; README links.
HarmonyOS reader-aligned effects (cumulative in 1.3.1) #
- Roadmap and capability-boundary statement: pre-rendered motion graphics
(GIF/APNG/frame sets), not realtime interactive rendering; realtime path
tracked in
doc/roadmap.md. - Flutter companion package
comic_motion_flutter: one-way dependency on the core (core stays pure Dart).MotionGifView(placeholder crossfade, playback control, reduce-motion, entrance frames) andParallaxGyroView(gyro/touch/injected-stream parallax) + power-aware strategy +PageCurlViewsimulated page-curl turn. See that package's CHANGELOG. - APNG streaming encoder:
OutputFormat.apng(opt-in) + top-levelencodeApng+StreamingApngBuilder(addFrame/addEncodedPngFrame/finish, O(single frame) memory) — reuses existing PNG IDAT payloads byte-for-byte; numPlays=0 loop semantics; parallel and serial output are byte-identical (test-locked);PipelineResult.outputApng/MemoryPipelineResult.apngBytes; plusdecodeApngScanlinesfor consumers and tests. - Entrance frame sequences:
exportEntranceFrames/exportEntranceFramesFile— blur-to-sharp reveal with optional zoom (default 12 frames); last frame is byte-identical to the working-resolution original (test-locked); index JSON carriesloop: false, holdOnLast: true. - Interactive parallax frame sets:
exportInteractionFrames/exportInteractionFramesFile—steps×axis(horizontal/vertical/both);FrameCompositor.parallaxOverride;MotionCacheManagerrecognizes all export kinds (kindfield);E_CANCELLED/E_TIMEOUTsemantics shared.
Runtime lifecycle (cumulative in 1.3.1) #
- In-memory pipeline:
processBytes({required input, maxPixels})→MemoryPipelineResult(gifBytes/paramsJsonBytes, no paths); shared_runCorekeeps memory and on-disk output byte-identical. - Cancellation / progress / timeout / keepPartial: checkpointed at the
main-isolate frame dispatch layer; timeout via deadline comparison; cancel
cleans partial output and throws
MotionCancelledException. Newlib/src/cancellation.dart. - Background isolate entry points:
processFileInBackground/processBytesInBackground— full pipeline including decode off the main isolate; exceptions re-thrown across isolates; README "thread model" warning added;example/04demo. - Concurrency guard (opt-in):
MotionPipelineGuardprocess-level FIFO semaphore (configure(maxConcurrent), default 1). - One-shot render params: top-level
dither/qualityTier/amplitude/directionDeg(null = untouched; byte-equal serialization and configHash vs explicit nesting, matrix-tested). - Effect selection API:
withEffect/withoutEffect/withEffects/clearEffects— immutable, always return new instances. - Param catalog:
ParamSpec+kRenderParamSpecs+kEffectNamesfor app-side dynamic settings panels; persistence viaEffectConfig.toJsonString()round-trip. - Webtoon strip mode:
StripSplitter(viewport-ratio slices, default 9:16, optional overlap) +processStrip(per-slice standard pipeline,<stem>_slice<NNN>_<hash8>/dirs, slice-level configHash) +kStripSafeEffectswhitelist;MotionPipeline.processImageas the decoded input entry; oversized tall images hintE_TOO_LARGEwith strip guidance. - Input format matrix (docs): JPEG/PNG/static WebP; animated WebP/GIF
decode to first frame (test-locked behavior contract); AVIF/HEIF rejected
with
E_DECODE_CORRUPT. - Cost estimation:
estimateCost(config, {sourceWidth, sourceHeight})→ memory/time ranges + working resolution + frame count. - Mobile reference ranges (docs): draft/typical/low-end tiers in the README, explicitly not device-calibrated.
Correctness & output consumption (cumulative in 1.3.1) #
- Content-hash cache keying: output dirs renamed to
<stem>_<contentHash8>_<configHash8>(strip slices included); re-downloaded files no longer hit stale renders.PipelineResult.contentHashadded. - First/still frame APIs:
renderStillFrame(File)(single-frame render, skips GIF encode; t=0 pixel-identical to first frame) + optionalincludeFirstFrameon all entry points. - Cache manager:
MotionCacheManager(listEntries / totalSize / purgeLRU / purgePrefix / purgeAll; only deletes contract-matching dirs). - Frame-stream callback:
onFrame(frameIndex, pngBytes)— ordered, exactly once per frame, same bytes asframes/frame_NNNN.png. - GIF rect diffing (opt-in):
EncodingParams.encoding.diffMode: none|rect(conditionally serialized; default keeps fingerprints unchanged); quantized-index diffing with disposal=1 sub-rect LZW encoding; GIF89a-spec reference decoder in tests; strip slices benefit automatically. - Web support status (docs): not supported — dart:io and Isolate.spawn dependencies documented.
- rich tier: documented as byte-equal to
standard;RenderTier.richdeprecated (JSON still parses for compatibility). - GIF playback guide (docs): Flutter-side consumption chapter (Image/extended_image/instantiateImageCodec trade-offs, decode memory levers, cover placeholders, warm-up/pause/reduce-motion strategies).
- Release-ready packaging: package docs moved to
doc/per pub layout.
⚠ BREAKING CHANGE #
- Output directory naming:
<stem>_<configHash8>→<stem>_<contentHash8>_<configHash8>(strip slices included). Old dirs are not recognized by the new layout; clean old caches on the consumer side. EffectConfig.fromFilemoved to the IO boundary: use the top-leveleffectConfigFromFile(path)(barrel-exported; error wrapping and codes unchanged).effect_config.dartis now pure Dart (no dart:io).EngineWorkerException.codechanged from a static constant to an instance field (value stillE_WORKER_CRASH).
1.3.0 #
See doc/CHANGELOG_zh-CN.md (Chinese changelog).