A mizchi/image extension for multi-frame GIF89a composition, palette quantization, and streaming LZW.
moon add mizchi/image@0.4.3
moon add WGYo90/moonbit-gif@0.1.0import {
"mizchi/image" @image,
"WGYo90/moonbit-gif" @gif,
}
let image = @image.ImageData::{
width: 32,
height: 32,
data: Bytes::make(32 * 32 * 4, 255),
}
let bytes = @gif.encode_image_sequence([image], delay_cs=8, loop_count=0)moon run --target native cmd/main
moon run --target native cmd/benchmarkpowershell -NoProfile -ExecutionPolicy Bypass -File scripts/bench.ps1 -Repeat 5
powershell -NoProfile -ExecutionPolicy Bypass -File scripts/bench.ps1 -Release -Repeat 5frames=12 size=128x96 encoded=1929 estimated=1916 decoded=12 pixels=147456 roundtrip=true palette_error=120
lzw_saving_per_mille=985
debug: runs=5 avg_ms=691.68 median_ms=693.75 min_ms=662.94 max_ms=731.71
release: runs=5 avg_ms=524.35 median_ms=540.81 min_ms=469.77 max_ms=563.18| 层 | 主要模块 | 作用 |
|---|---|---|
| 数据模型 | gif_types.mbt, gif_bytes.mbt, image_bridge.mbt | RGBA、画布、帧、动画与 ImageData 适配 |
| 格式层 | gif_decoder.mbt, gif_encoder.mbt, gif_formats.mbt, gif_metadata.mbt | GIF block、调色板、扩展块和元数据 |
| 压缩层 | lzw.mbt, lzw_analysis.mbt, gif_stream.mbt, gif_streaming.mbt | 位级 LZW、子块封装、分块收集和压缩分析 |
| 质量层 | palette.mbt, palette_tools.mbt, quantization_extra.mbt, palette_mapping.mbt | Median Cut、抖动、索引映射和量化质量 |
| 动画层 | compose.mbt, animation_*.mbt, gif_delta*.mbt, gif_optimizer.mbt | disposal 合成、时间线、差分帧和大小优化 |
| 画布层 | canvas_*.mbt, color_*.mbt, draw_primitives.mbt | 几何、滤镜、绘制、颜色空间、统计和图案 |
| 应用层 | gif_workflow.mbt, gif_cli_*.mbt, gif_report.mbt, gif_preflight.mbt | 声明式导出、CLI 请求、预检、报告和限制 |
moon fmt --check
moon check --target all --deny-warn --fmt
moon info --target all
moon test --target all --deny-warn
moon build --target all --deny-warnpowershell -NoProfile -ExecutionPolicy Bypass -File scripts/check.ps1pub(all) struct Affine2i {
a : Int
b : Int
c : Int
d : Int
tx : Int
ty : Int
scale : Int
} derive(Debug)fn Animation::contact_sheet(self : Animation, options? : ContactSheetOptions, policy? : AnimationSamplingPolicy) -> Canvasfn Animation::export_report(self : Animation, profile : ExportProfile) -> ExportReport raise GifErrorfn Animation::moving_layer(self : Animation, layer : ImageLayer, start_left : Int, start_top : Int, end_left : Int, end_top : Int) -> Animationfn Animation::optimization_report(self : Animation, options : OptimizationOptions) -> OptimizationReport raise GifErrorfn Animation::optimization_warnings(self : Animation, options : OptimizationOptions) -> Array[String]fn Animation::optimize_to_budget(self : Animation, budget_bytes : Int, options : OptimizationOptions) -> (Animation, OptimizationReport) raise GifErrorfn Animation::overlay_static(self : Animation, overlay : Canvas, alignment : CompositeAlignment) -> Animationfn Animation::profile_for_budget(self : Animation, budget_bytes : Int) -> ExportProfile raise GifErrorfn Animation::quantize_delays(self : Animation, tick_cs : Int, policy : TimelinePolicy) -> Animationfn Animation::resample_frames_with_filter(self : Animation, width : Int, height : Int, filter? : ResampleFilter) -> Animationfn AnimationBuilder::solid_frame(self : AnimationBuilder, color~ : Rgba, delay_cs~ : Int) -> AnimationBuilderpub(all) struct AnimationComplexity {
frame_count : Int
total_pixels : Int
changed_pixels : Int
mean_unique_colors : Int
active_frames : Int
peak_entropy : Int
} derive(Debug)fn AnimationEditor::transform(self : AnimationEditor, transform : AnimationTransform) -> AnimationEditorpub(all) struct AnimationGraph {
width : Int
height : Int
nodes : Array[AnimationNode]
background : Rgba
} derive(Debug)fn AnimationGraph::replace(self : AnimationGraph, name : String, node : AnimationNode) -> AnimationGraphpub(all) struct AnimationPipeline {
stages : Array[PipelineStage]
delay_override : Int?
} derive(Debug)fn AnimationPipeline::apply_preserving_metadata(self : AnimationPipeline, animation : Animation) -> Animationpub(all) struct AnimationQuality {
compared_frames : Int
mean_error : Int
maximum_error : Int
differing_frames : Int
mean_psnr_milli : Int
} derive(Debug)pub(all) struct AnimationStats {
frame_count : Int
duration_cs : Int
transparent_frames : Int
changed_pixels : Int
} derive(Debug)pub(all) struct AnimationTimeline {
animation : Animation
markers : Array[TimelineMarker]
} derive(Debug)fn AnimationTimeline::between(self : AnimationTimeline, start_cs : Int, end_cs : Int) -> Array[TimelineMarker]fn AnimationTimeline::mark_frame(self : AnimationTimeline, name : String, index : Int, color : Rgba) -> AnimationTimelinefn AtlasPackOptions::page_size(self : AtlasPackOptions, width : Int, height : Int) -> AtlasPackOptionspub(all) struct AtlasPage {
index : Int
canvas : Canvas
placements : Array[SpritePlacement]
} derive(Debug)pub(all) struct BenchmarkResult {
workload : BenchmarkWorkload
encoded_bytes : Int
estimated_bytes : Int
decoded_frames : Int
source_pixels : Int
roundtrip_equal : Bool
palette_error : Int
lzw_saving_per_mille : Int
} derive(Debug)pub(all) struct BenchmarkWorkload {
width : Int
height : Int
frames : Int
palette_colors : Int
delay_cs : Int
} derive(Debug)fn BenchmarkWorkload::size(self : BenchmarkWorkload, width : Int, height : Int) -> BenchmarkWorkloadfn Canvas::close_mask(self : Canvas, radius : Int, mode? : MorphologyMode, threshold? : Byte) -> Canvasfn Canvas::colorize_channel(self : Canvas, channel : ColorChannel, tint : Rgba, strength : Int) -> Canvasfn Canvas::components(self : Canvas, threshold? : Byte, minimum_pixels? : Int) -> Array[ComponentInfo]fn Canvas::fit_resample(self : Canvas, width : Int, height : Int, filter : ResampleFilter) -> Canvasfn Canvas::gradient_stops(self : Canvas, mode : GradientMode, stops : ArrayView[GradientStop]) -> Canvasfn Canvas::map_channel(self : Canvas, channel : ColorChannel, gain_per_mille : Int, bias : Int) -> Canvasfn Canvas::open_mask(self : Canvas, radius : Int, mode? : MorphologyMode, threshold? : Byte) -> Canvasfn Canvas::outline(self : Canvas, radius? : Int, mode? : MorphologyMode, threshold? : Byte) -> Canvasfn Canvas::resample_with_report(self : Canvas, width~ : Int, height~ : Int, filter? : ResampleFilter) -> (Canvas, ResampleReport)pub(all) struct CanvasComplexity {
pixels : Int
opaque_pixels : Int
unique_colors : Int
edge_pixels : Int
entropy_milli : Int
luma_range : Int
} derive(Debug)pub(all) struct CanvasDigest {
hash : UInt
pixels : Int
width : Int
height : Int
alpha_pixels : Int
} derive(Debug)fn CanvasPipeline::apply_animation_fit(self : CanvasPipeline, animation : Animation, width : Int, height : Int) -> Animationpub(all) struct CanvasStats {
pixel_count : Int
opaque_count : Int
transparent_count : Int
minimum_luma : Byte
maximum_luma : Byte
average_luma : Int
} derive(Debug)pub(all) struct ChannelStats {
channel : ColorChannel
minimum : Byte
maximum : Byte
mean : Int
nonzero : Int
histogram : Array[Int]
} derive(Debug)pub(all) struct ColorAdjustment {
brightness : Int
contrast : Int
saturation : Int
hue_degrees : Int
alpha : Int
} derive(Debug)pub(all) struct ComponentInfo {
label : Int
pixels : Int
left : Int
top : Int
right : Int
bottom : Int
} derive(Debug)pub(all) struct CompositeOptions {
alignment : CompositeAlignment
gap : Int
background : Rgba
loop_to_longest : Bool
} derive(Debug)fn CompositeOptions::alignment(self : CompositeOptions, value : CompositeAlignment) -> CompositeOptionsfn ContactSheetOptions::cell(self : ContactSheetOptions, width : Int, height : Int) -> ContactSheetOptionsfn ContactSheetOptions::with_labels(self : ContactSheetOptions, enabled : Bool) -> ContactSheetOptionsfn ConvolutionKernel::new(width~ : Int, height~ : Int, weights~ : ArrayView[Int], divisor~ : Int, bias~ : Int) -> ConvolutionKernelpub(all) struct DeltaPlan {
policy : DeltaPolicy
items : Array[DeltaPlanItem]
full_pixels : Int
selected_pixels : Int
} derive(Debug)pub(all) struct DeltaStats {
frame_count : Int
full_pixels : Int
delta_pixels : Int
changed_frames : Int
} derive(Debug)pub(all) struct EditReport {
input_frames : Int
output_frames : Int
removed_frames : Int
duration_before : Int
duration_after : Int
} derive(Debug)pub(all) struct EncodeOptions {
max_colors : Int
default_delay_cs : Int
emit_loop_extension : Bool
} derive(Debug)pub(all) struct EncodeOutcome {
bytes : Bytes?
error_code : String?
error_message : String?
} derive(Debug)pub(all) struct ExportPlan {
profile : ExportProfile
optimization : OptimizationOptions
encode : EncodeOptions
estimated_bytes : Int
quality_target : Int
} derive(Debug)pub(all) struct ExportReport {
profile : ExportProfile
input_frames : Int
output_frames : Int
estimated_bytes : Int
actual_bytes : Int
retained_pixels_per_mille : Int
quality_target : Int
} derive(Debug)pub(all) struct FrameEstimate {
width : Int
height : Int
palette_bytes : Int
lzw_bytes : Int
total_bytes : Int
} derive(Debug)pub(all) struct GifByteIndex {
bytes : Int
header_bytes : Int
trailer_offset : Int?
frames : Array[GifIndexEntry]
extension_count : Int
} derive(Debug)pub(all) struct GifCliPlan {
command : GifCommand
animation : Animation?
encode_options : EncodeOptions
optimization_options : OptimizationOptions
preview_size : (Int, Int)?
} derive(Debug)pub(all) struct GifCliRequest {
command : GifCommand
input : String
output : String
width : Int?
height : Int?
colors : Int
delay_cs : Int
profile : ExportProfile
} derive(Debug)pub(all) struct GifCliResult {
command : GifCommand
bytes : Bytes?
animation : Animation?
report : GifDocumentReport?
messages : Array[String]
} derive(Debug)pub(all) struct GifDiagnostic {
level : DiagnosticLevel
code : String
message : String
frame_index : Int?
} derive(Debug)pub(all) struct GifDocumentReport {
bytes : Int
width : Int
height : Int
frames : Int
duration_cs : Int
encoded_pixels : Int
unique_colors : Int
entropy_milli : Int
estimated_bytes : Int
validation_errors : Int
validation_warnings : Int
complete_index : Bool
} derive(Debug)pub(all) struct GifHeader {
version : GifVersion
width : Int
height : Int
global_palette : Bool
color_resolution : Int
sorted_palette : Bool
background_index : Int
aspect_ratio : Int
} derive(Debug)pub(all) struct GifIndexEntry {
frame_index : Int
marker_offset : Int
descriptor_offset : Int
extension_offset : Int?
} derive(Debug)pub(all) struct GifInfo {
version : String
width : Int
height : Int
frame_count : Int
has_global_palette : Bool
uses_local_palettes : Bool
uses_transparency : Bool
loop_count : Int?
compressed_bytes : Int
} derive(Debug)pub(all) struct GifLimits {
max_dimension : Int
max_colors : Int
max_delay_cs : Int
max_frames : Int
max_comment_bytes : Int
} derive(Debug)fn GifMetadata::application(identifier : String, authentication : String, payload : Bytes) -> GifMetadatapub(all) struct GifValidationReport {
diagnostics : Array[GifDiagnostic]
frame_count : Int
duration_cs : Int
pixel_count : Int
} derive(Debug)pub(all) struct GifWorkflow {
steps : Array[GifWorkflowStep]
profile : ExportProfile
limits : GifLimits
comment : String?
reject_empty : Bool
} derive(Debug)fn GifWorkflow::crop(self : GifWorkflow, x : Int, y : Int, width : Int, height : Int) -> GifWorkflowfn GifWorkflow::encode_report(self : GifWorkflow, animation : Animation) -> GifWorkflowReport raise GifErrorfn GifWorkflow::estimate(self : GifWorkflow, animation : Animation) -> GifWorkflowReport raise GifErrorfn GifWorkflow::report(self : GifWorkflow, animation : Animation) -> GifWorkflowReport raise GifErrorpub(all) struct GifWorkflowReport {
valid : Bool
issues : Array[String]
input_width : Int
input_height : Int
output_width : Int
output_height : Int
input_frames : Int
output_frames : Int
input_pixels : Int
output_pixels : Int
encoded_bytes : Int
estimated_bytes : Int
comment_bytes : Int
stage_count : Int
profile : ExportProfile
} derive(Debug)pub(all) enum GifWorkflowStep {
Visual(stage~ : PipelineStage)
Resize(width~ : Int, height~ : Int)
SetDelay(delay_cs~ : Int)
SetLoop(loop_count~ : Int?)
SetBackground(color~ : Rgba)
SetProfile(profile~ : ExportProfile)
ClampLimits
NormalizeTimeline
} derive(Debug)pub(all) struct LayerStack {
width : Int
height : Int
background : Rgba
layers : Array[ImageLayer]
} derive(Debug)pub(all) struct LzwRoundtripReport {
input_bytes : Int
compressed_bytes : Int
decoded_bytes : Int
minimum_code_size : Int
exact : Bool
saving_per_mille : Int
} derive(Debug)pub(all) struct LzwStats {
input_bytes : Int
compressed_bytes : Int
minimum_code_size : Int
} derive(Debug)pub(all) struct MotionKeyframe {
time_cs : Int
x : Int
y : Int
scale_per_mille : Int
rotation : CanvasRotation
opacity : Byte
} derive(Debug)fn MotionKeyframe::new(time_cs~ : Int, x~ : Int, y~ : Int, scale_per_mille~ : Int, rotation~ : CanvasRotation, opacity~ : Byte) -> MotionKeyframepub(all) struct MotionSample {
x : Int
y : Int
scale_per_mille : Int
opacity : Byte
rotation : CanvasRotation
} derive(Debug)pub(all) struct NetpbmOptions {
format : NetpbmFormat
max_value : Int
include_comments : Bool
} derive(Debug)pub(all) struct OptimizationFrame {
index : Int
source_pixels : Int
encoded_pixels : Int
palette_colors : Int
compressed_bytes : Int
estimated_bytes : Int
changed : Bool
} derive(Debug)pub(all) struct OptimizationOptions {
max_colors : Int
difference_threshold : Int
merge_equal_frames : Bool
crop_delta_frames : Bool
palette_strategy : PaletteStrategy
default_delay_cs : Int
} derive(Debug)fn OptimizationOptions::crop_deltas(self : OptimizationOptions, enabled : Bool) -> OptimizationOptionsfn OptimizationOptions::merge_equal(self : OptimizationOptions, enabled : Bool) -> OptimizationOptionsfn OptimizationOptions::palette(self : OptimizationOptions, strategy : PaletteStrategy) -> OptimizationOptionspub(all) struct OptimizationReport {
input_frames : Int
output_frames : Int
input_pixels : Int
output_pixels : Int
palette_bytes : Int
compressed_bytes : Int
estimated_bytes : Int
changed_frames : Int
frames : Array[OptimizationFrame]
} derive(Debug)pub(all) struct PalettePlan {
mode : PalettePlanMode
max_colors : Int
transparent : Rgba?
} derive(Debug)pub(all) struct PaletteQuality {
palette_size : Int
pixels : Int
mean_error : Int
maximum_error : Int
exact_pixels : Int
} derive(Debug)pub(all) struct PatternOptions {
kind : PatternKind
scale : Int
seed : Int
first : Rgba
second : Rgba
} derive(Debug)pub(all) enum PipelineStage {
Gray
InvertColors
Blur(radius~ : Int)
GaussianBlur
Sharpen
Edge
Posterize(levels~ : Int)
Threshold(level~ : Byte)
Normalize
Dither(levels~ : Int)
Pixelate(block~ : Int)
Grain(amount~ : Int, seed~ : Int)
Tint(color~ : Rgba, strength~ : Int)
Adjust(adjustment~ : ColorAdjustment)
Convolve(kernel~ : ConvolutionKernel)
Dilate(radius~ : Int, mode~ : MorphologyMode)
Erode(radius~ : Int, mode~ : MorphologyMode)
ChromaKey(key~ : Rgba, tolerance~ : Int)
Crop(x~ : Int, y~ : Int, width~ : Int, height~ : Int)
Resize(width~ : Int, height~ : Int)
FlipHorizontal
FlipVertical
} derive(Debug)pub(all) struct PlaybackSnapshot {
time_cs : Int
frame_index : Int
progress_per_mille : Int
playing : Bool
} derive(Debug)pub(all) struct PlaybackState {
animation : Animation
mode : PlaybackMode
time_cs : Int
direction : Int
playing : Bool
} derive(Debug)pub(all) struct QualityMetrics {
pixels : Int
differing_pixels : Int
mean_squared_error : Int
maximum_error : Int
alpha_error : Int
psnr_milli : Int
} derive(Debug)pub(all) struct ResampleReport {
input_width : Int
input_height : Int
output_width : Int
output_height : Int
filter : ResampleFilter
input_pixels : Int
output_pixels : Int
} derive(Debug)pub(all) struct ScalePlan {
source_width : Int
source_height : Int
target_width : Int
target_height : Int
scale_per_mille : Int
crop : Bool
} derive(Debug)pub(all) struct SpriteAtlas {
pages : Array[AtlasPage]
placements : Array[SpritePlacement]
page_width : Int
page_height : Int
} derive(Debug)pub(all) struct SpritePlacement {
name : String
page : Int
x : Int
y : Int
width : Int
height : Int
source_width : Int
source_height : Int
rotated : Bool
} derive(Debug)pub(all) struct TimelineMetrics {
frames : Int
duration_cs : Int
minimum_delay_cs : Int
maximum_delay_cs : Int
mean_delay_cs : Int
empty_frames : Int
active_pixels : Int
} derive(Debug)pub(all) struct TransitionOptions {
kind : TransitionKind
steps : Int
delay_cs : Int
seed : Int
} derive(Debug)fn animation_from_patches(width~ : Int, height~ : Int, background~ : Rgba, patches : ArrayView[PatchFrame], loop_count? : Int?) -> Animationfn apply_patches(width~ : Int, height~ : Int, background~ : Rgba, patches : ArrayView[PixelPatch]) -> Canvasfn can_roundtrip(data : Bytes) -> Boolfn centiseconds_to_milliseconds(value : Int) -> Intfn choose_palette_budget(animation : Animation, budget_bytes : Int, options : OptimizationOptions) -> Int raise GifErrorfn clamp_palette_size(size : Int) -> Intfn composite_animations(base : Animation, overlay : Animation, options? : CompositeOptions) -> Animationfn composite_three(base : Animation, middle : Animation, front : Animation, options : CompositeOptions) -> Animationfn encode_batch(items : ArrayView[BatchItem], profile : ExportProfile) -> (Array[(String, Bytes)], BatchReport)fn encode_chunks(animation : Animation, options? : EncodeOptions, chunk_size? : Int) -> Array[GifChunk] raise GifErrorfn encode_stream(animation : Animation, options? : EncodeOptions, chunk_size? : Int) -> GifByteStream raise GifErrorfn encode_with_application(animation : Animation, identifier : String, authentication : String, payload : Bytes, options? : EncodeOptions) -> Bytes raise GifErrorfn encode_with_comment(animation : Animation, comment : String, options? : EncodeOptions) -> Bytes raise GifErrorfn encode_with_metadata(animation : Animation, metadata : ArrayView[GifMetadata], options? : EncodeOptions) -> Bytes raise GifErrorfn explain_gif_error(data : Bytes) -> Stringfn grid_atlas(frames : ArrayView[Canvas], columns : Int, cell_width : Int, cell_height : Int, padding : Int) -> SpriteAtlasfn has_gif_signature(data : Bytes) -> Boolfn is_valid(data : Bytes) -> Boolfn layout_canvases(canvases : ArrayView[Canvas], direction : LayoutDirection, gap : Int, background : Rgba) -> LayoutResultfn lzw_roundtrip(indices : ArrayView[Byte], minimum_code_size : Int) -> LzwRoundtripReport raise GifErrorfn make_scale_plan(source_width : Int, source_height : Int, target_width : Int, target_height : Int, crop : Bool) -> ScalePlanfn milliseconds_to_centiseconds(value : Int) -> Intfn minimum_code_size_for_colors(colors : Int) -> Intfn pack_sub_blocks(payload : Bytes, block_size? : Int) -> Bytesfn procedural_animation(width : Int, height : Int, frames : Int, options : PatternOptions, delay_cs? : Int) -> Animationfn quantize_dithered(canvas : Canvas, max_colors : Int, transparent : Rgba?, mode : DitherMode) -> QuantizedFramefn recommended_chunk_size(packet_budget : Int) -> Intfn render_layers(width : Int, height : Int, stacks : ArrayView[LayerStack], delay_cs? : Int) -> Animationfn report_animation(animation : Animation, options : OptimizationOptions) -> GifDocumentReport raise GifErrorfn star_points(center_x : Int, center_y : Int, outer_radius : Int, inner_radius : Int, points : Int) -> Array[(Int, Int)]fn validate_and_repair(animation : Animation) -> (GifValidationReport, Animation, GifValidationReport)Install
Download zipA mizchi/image extension for multi-frame GIF89a composition, palette quantization, and streaming LZW.