blecsd-media
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
Chinese@blecsd/media Package Skill
@blecsd/media 包技能
The package provides image parsing, rendering, and video playback for blECSd terminal applications. It includes complete GIF and PNG parsers, ANSI rendering to 256-color terminal, animated image widgets, video player integration (mpv/mplayer), and W3M overlay support.
@blecsd/mediaInstall:
Peer dependency:
pnpm add @blecsd/mediablecsd >= 0.6.0@blecsd/media安装:
对等依赖:
pnpm add @blecsd/mediablecsd >= 0.6.0Subpath Imports
子路径导入
The package exposes focused subpath imports:
typescript
import { parseGIF, frameToRGBA } from '@blecsd/media/gif';
import { parsePNG, extractPixels } from '@blecsd/media/png';
import { renderToAnsi, scaleBitmap, rgbTo256Color } from '@blecsd/media/render';
import { createImage, setImageData, play, pause } from '@blecsd/media/widgets/image';
import { createVideo, detectVideoPlayer } from '@blecsd/media/widgets/video';
import { createW3MOverlay } from '@blecsd/media/overlay';Or use the namespace API from the main entry:
typescript
import { gif, png, ansiRender, imageWidget, videoWidget, w3m } from '@blecsd/media';该包提供聚焦的子路径导入:
typescript
import { parseGIF, frameToRGBA } from '@blecsd/media/gif';
import { parsePNG, extractPixels } from '@blecsd/media/png';
import { renderToAnsi, scaleBitmap, rgbTo256Color } from '@blecsd/media/render';
import { createImage, setImageData, play, pause } from '@blecsd/media/widgets/image';
import { createVideo, detectVideoPlayer } from '@blecsd/media/widgets/video';
import { createW3MOverlay } from '@blecsd/media/overlay';或者从主入口使用命名空间API:
typescript
import { gif, png, ansiRender, imageWidget, videoWidget, w3m } from '@blecsd/media';GIF Parser
GIF解析器
Complete GIF parser with LZW decompression and animation support.
typescript
import { parseGIF, frameToRGBA, validateGIFSignature } from '@blecsd/media/gif';
// Read file
const buffer = await fs.readFile('animation.gif');
// Validate
if (!validateGIFSignature(buffer)) throw new Error('Not a GIF');
// Parse
const result = parseGIF(buffer);
// result: { header, frames[], globalColorTable }
// Get RGBA pixels for a frame
const rgba = frameToRGBA(result.frames[0]);
// rgba: Uint8Array of [r, g, b, a, r, g, b, a, ...]Key functions:
- - Parse full GIF with animation frames
parseGIF(buffer) - - Parse header only
parseGIFHeader(buffer) - - Check GIF87a/GIF89a signature
validateGIFSignature(buffer) - - Convert frame to RGBA pixel data
frameToRGBA(frame) - - Deinterlace interlaced frames
deinterlace(pixels, width, height) - - Parse color table
parseColorTable(buffer, offset, size) - - Read GIF sub-blocks
readSubBlocks(buffer, offset) - - LZW decompression
decompressLZW(data, minCodeSize) - /
createBitReader(data)- Bit-level readingreadCode(reader, codeSize)
支持LZW解压缩和动画的完整GIF解析器。
typescript
import { parseGIF, frameToRGBA, validateGIFSignature } from '@blecsd/media/gif';
// 读取文件
const buffer = await fs.readFile('animation.gif');
// 验证
if (!validateGIFSignature(buffer)) throw new Error('不是GIF文件');
// 解析
const result = parseGIF(buffer);
// result: { header, frames[], globalColorTable }
// 获取某一帧的RGBA像素数据
const rgba = frameToRGBA(result.frames[0]);
// rgba: Uint8Array 格式为 [r, g, b, a, r, g, b, a, ...]核心函数:
- - 解析包含动画帧的完整GIF
parseGIF(buffer) - - 仅解析头部
parseGIFHeader(buffer) - - 检查GIF87a/GIF89a签名
validateGIFSignature(buffer) - - 将帧转换为RGBA像素数据
frameToRGBA(frame) - - 对隔行扫描的帧进行去隔行处理
deinterlace(pixels, width, height) - - 解析颜色表
parseColorTable(buffer, offset, size) - - 读取GIF子块
readSubBlocks(buffer, offset) - - LZW解压缩
decompressLZW(data, minCodeSize) - /
createBitReader(data)- 位级读取readCode(reader, codeSize)
PNG Parser
PNG解析器
Complete PNG parser with filter reconstruction.
typescript
import { parsePNG, extractPixels, parseChunks } from '@blecsd/media/png';
const buffer = await fs.readFile('image.png');
const result = parsePNG(buffer);
// result: { header (IHDR), pixels, chunks[] }
// Or parse step by step
const chunks = parseChunks(buffer);
const header = parseIHDR(chunks[0].data);
const pixels = extractPixels(chunks);Key functions:
- - Parse full PNG
parsePNG(buffer) - - Parse PNG chunks
parseChunks(buffer) - - Parse image header (width, height, bitDepth, colorType)
parseIHDR(data) - - Reconstruct PNG filters
reconstructFilters(scanlines, width, height, bitDepth) - - PNG Paeth predictor
paethPredictor(a, b, c) - - Extract raw pixel data
extractPixels(chunks) - - Parse palette chunk
parsePLTE(buffer)
支持过滤器重建的完整PNG解析器。
typescript
import { parsePNG, extractPixels, parseChunks } from '@blecsd/media/png';
const buffer = await fs.readFile('image.png');
const result = parsePNG(buffer);
// result: { header (IHDR), pixels, chunks[] }
// 或者分步解析
const chunks = parseChunks(buffer);
const header = parseIHDR(chunks[0].data);
const pixels = extractPixels(chunks);核心函数:
- - 解析完整PNG
parsePNG(buffer) - - 解析PNG块
parseChunks(buffer) - - 解析图像头部(宽度、高度、位深度、颜色类型)
parseIHDR(data) - - 重建PNG过滤器
reconstructFilters(scanlines, width, height, bitDepth) - - PNG Paeth预测器
paethPredictor(a, b, c) - - 提取原始像素数据
extractPixels(chunks) - - 解析调色板块
parsePLTE(buffer)
ANSI Rendering
ANSI渲染
Convert bitmap data to terminal-renderable ANSI output.
typescript
import { renderToAnsi, scaleBitmap, cellMapToString, rgbTo256Color } from '@blecsd/media/render';
// Render bitmap to ANSI cells
const cellMap = renderToAnsi(bitmap, {
width: 40, // Target width in terminal columns
height: 20, // Target height in terminal rows
mode: 'halfblock' // 'halfblock' | 'braille' | 'ascii'
});
// Convert to string for output
const output = cellMapToString(cellMap);
// Scale a bitmap
const scaled = scaleBitmap(bitmap, 40, 20);
// Color conversion
const color256 = rgbTo256Color(255, 128, 0); // RGB to 256-color palette
const lum = rgbLuminance(255, 128, 0); // Compute luminance
const char = luminanceToChar(lum); // Map to ASCII char
// Blend colors
const blended = blendWithBackground([255, 128, 0], [0, 0, 0]);RenderMode options:
- - Uses half-block characters for 2 pixels per cell vertically
'halfblock' - - Uses braille characters for 2x4 pixels per cell (highest density)
'braille' - - Uses ASCII characters mapped by luminance
'ascii'
将位图数据转换为可在终端渲染的ANSI输出。
typescript
import { renderToAnsi, scaleBitmap, cellMapToString, rgbTo256Color } from '@blecsd/media/render';
// 将位图渲染为ANSI单元格
const cellMap = renderToAnsi(bitmap, {
width: 40, // 终端列数目标宽度
height: 20, // 终端行数目标高度
mode: 'halfblock' // 'halfblock' | 'braille' | 'ascii'
});
// 转换为字符串用于输出
const output = cellMapToString(cellMap);
// 缩放位图
const scaled = scaleBitmap(bitmap, 40, 20);
// 颜色转换
const color256 = rgbTo256Color(255, 128, 0); // RGB转256色调色板
const lum = rgbLuminance(255, 128, 0); // 计算亮度
const char = luminanceToChar(lum); // 映射为ASCII字符
// 颜色混合
const blended = blendWithBackground([255, 128, 0], [0, 0, 0]);渲染模式选项:
- - 使用半块字符,每个单元格垂直显示2个像素
'halfblock' - - 使用盲文字符,每个单元格显示2x4个像素(最高密度)
'braille' - - 使用按亮度映射的ASCII字符
'ascii'
Image Widget
图像组件
High-level widget for displaying static and animated images.
typescript
import { createImage, setImageData, play, pause, stop } from '@blecsd/media/widgets/image';
// Create image widget
const eid = createImage(world, {
position: { x: 0, y: 0 },
dimensions: { width: 40, height: 20 },
renderMode: 'halfblock',
});
// Set image data (from parsed GIF/PNG)
setImageData(eid, {
frames: gifResult.frames.map(f => frameToRGBA(f)),
width: gifResult.header.width,
height: gifResult.header.height,
frameCount: gifResult.frames.length,
delays: gifResult.frames.map(f => f.delay),
});
// Animation control (for animated GIFs)
play(eid);
pause(eid);
stop(eid);
// Set specific frame
setFrame(eid, 3);
// Get current state
const bitmap = getImageBitmap(eid);
const cellMap = getImageCellMap(eid);
// Aspect ratio helper
const { width, height } = calculateAspectRatioDimensions(
sourceWidth, sourceHeight,
targetWidth, targetHeight
);
// Cleanup
clearImageCache(eid);
clearAllImageCaches();
// Type guard
if (isImage(world, eid)) { /* ... */ }用于显示静态和动态图像的高级组件。
typescript
import { createImage, setImageData, play, pause, stop } from '@blecsd/media/widgets/image';
// 创建图像组件
const eid = createImage(world, {
position: { x: 0, y: 0 },
dimensions: { width: 40, height: 20 },
renderMode: 'halfblock',
});
// 设置图像数据(来自解析后的GIF/PNG)
setImageData(eid, {
frames: gifResult.frames.map(f => frameToRGBA(f)),
width: gifResult.header.width,
height: gifResult.header.height,
frameCount: gifResult.frames.length,
delays: gifResult.frames.map(f => f.delay),
});
// 动画控制(针对GIF动图)
play(eid);
pause(eid);
stop(eid);
// 设置特定帧
setFrame(eid, 3);
// 获取当前状态
const bitmap = getImageBitmap(eid);
const cellMap = getImageCellMap(eid);
// 宽高比辅助工具
const { width, height } = calculateAspectRatioDimensions(
sourceWidth, sourceHeight,
targetWidth, targetHeight
);
// 清理
clearImageCache(eid);
clearAllImageCaches();
// 类型守卫
if (isImage(world, eid)) { /* ... */ }Video Widget
视频组件
Video playback using external players (mpv or mplayer).
typescript
import { createVideo, detectVideoPlayer, getVideoPlaybackState } from '@blecsd/media/widgets/video';
// Auto-detect available video player
const player = detectVideoPlayer(); // 'mpv' | 'mplayer' | undefined
// Create video widget
const eid = createVideo(world, {
position: { x: 0, y: 0 },
dimensions: { width: 80, height: 24 },
source: '/path/to/video.mp4',
player: player, // 'mpv' | 'mplayer'
autoplay: true,
});
// Check state
const state = getVideoPlaybackState(eid); // 'stopped' | 'playing' | 'paused'
// Get detected player
const detectedPlayer = getVideoPlayer(eid);
// Build command args (for manual control)
const args = buildMpvArgs({ source: 'video.mp4', width: 80, height: 24 });
const args2 = buildMplayerArgs({ source: 'video.mp4' });
const args3 = buildPlayerArgs({ source: 'video.mp4', player: 'mpv' });
// Send commands to running player
sendPauseCommand(handle);
sendSeekCommand(handle, 10); // Seek 10 seconds
// Type guard
if (isVideo(world, eid)) { /* ... */ }使用外部播放器(mpv或mplayer)进行视频播放。
typescript
import { createVideo, detectVideoPlayer, getVideoPlaybackState } from '@blecsd/media/widgets/video';
// 自动检测可用的视频播放器
const player = detectVideoPlayer(); // 'mpv' | 'mplayer' | undefined
// 创建视频组件
const eid = createVideo(world, {
position: { x: 0, y: 0 },
dimensions: { width: 80, height: 24 },
source: '/path/to/video.mp4',
player: player, // 'mpv' | 'mplayer'
autoplay: true,
});
// 检查状态
const state = getVideoPlaybackState(eid); // 'stopped' | 'playing' | 'paused'
// 获取检测到的播放器
const detectedPlayer = getVideoPlayer(eid);
// 构建命令参数(用于手动控制)
const args = buildMpvArgs({ source: 'video.mp4', width: 80, height: 24 });
const args2 = buildMplayerArgs({ source: 'video.mp4' });
const args3 = buildPlayerArgs({ source: 'video.mp4', player: 'mpv' });
// 向运行中的播放器发送命令
sendPauseCommand(handle);
sendSeekCommand(handle, 10); // 快进10秒
// 类型守卫
if (isVideo(world, eid)) { /* ... */ }W3M Overlay
W3M覆盖层
Use W3M's terminal graphics protocol for high-quality image display.
typescript
import { createW3MOverlay } from '@blecsd/media/overlay';
const eid = createW3MOverlay(world, {
position: { x: 0, y: 0 },
dimensions: { width: 40, height: 20 },
imagePath: '/path/to/image.png',
});使用W3M的终端图形协议实现高质量图像显示。
typescript
import { createW3MOverlay } from '@blecsd/media/overlay';
const eid = createW3MOverlay(world, {
position: { x: 0, y: 0 },
dimensions: { width: 40, height: 20 },
imagePath: '/path/to/image.png',
});Common Patterns
常见使用模式
Display a GIF in Terminal
在终端中显示GIF
typescript
import { parseGIF, frameToRGBA } from '@blecsd/media/gif';
import { renderToAnsi, cellMapToString, scaleBitmap } from '@blecsd/media/render';
const buffer = await fs.readFile('cat.gif');
const gif = parseGIF(buffer);
for (const frame of gif.frames) {
const rgba = frameToRGBA(frame);
const bitmap = { data: rgba, width: gif.header.width, height: gif.header.height };
const scaled = scaleBitmap(bitmap, 40, 20);
const cellMap = renderToAnsi(scaled, { mode: 'halfblock' });
console.log(cellMapToString(cellMap));
}typescript
import { parseGIF, frameToRGBA } from '@blecsd/media/gif';
import { renderToAnsi, cellMapToString, scaleBitmap } from '@blecsd/media/render';
const buffer = await fs.readFile('cat.gif');
const gif = parseGIF(buffer);
for (const frame of gif.frames) {
const rgba = frameToRGBA(frame);
const bitmap = { data: rgba, width: gif.header.width, height: gif.header.height };
const scaled = scaleBitmap(bitmap, 40, 20);
const cellMap = renderToAnsi(scaled, { mode: 'halfblock' });
console.log(cellMapToString(cellMap));
}Display a PNG
显示PNG
typescript
import { parsePNG } from '@blecsd/media/png';
import { renderToAnsi, cellMapToString } from '@blecsd/media/render';
const buffer = await fs.readFile('photo.png');
const png = parsePNG(buffer);
const cellMap = renderToAnsi(
{ data: png.pixels, width: png.header.width, height: png.header.height },
{ width: 60, height: 30, mode: 'braille' }
);
console.log(cellMapToString(cellMap));typescript
import { parsePNG } from '@blecsd/media/png';
import { renderToAnsi, cellMapToString } from '@blecsd/media/render';
const buffer = await fs.readFile('photo.png');
const png = parsePNG(buffer);
const cellMap = renderToAnsi(
{ data: png.pixels, width: png.header.width, height: png.header.height },
{ width: 60, height: 30, mode: 'braille' }
);
console.log(cellMapToString(cellMap));Best Practices
最佳实践
- Choose the right render mode. gives highest density (2x4 pixels per cell),
brailleis the default and works well for most images,halfblockis most compatible.ascii - Scale before rendering. Use to fit the image to your terminal dimensions before converting to ANSI.
scaleBitmap - Handle animated GIFs with the image widget. Don't manually loop frames; the widget handles animation timing.
- Video requires external players. checks for mpv or mplayer. If neither is found, video won't work.
detectVideoPlayer() - W3M overlay requires w3mimgdisplay. This is a separate binary from w3m.
- Clean up image caches. Call or
clearImageCache(eid)to free memory.clearAllImageCaches() - Use subpath imports for tree-shaking. Import from instead of
@blecsd/media/gifto only pull in what you need.@blecsd/media
- 选择合适的渲染模式。模式提供最高密度(每个单元格2x4像素),
braille是默认模式,适用于大多数图像,halfblock模式兼容性最好。ascii - 渲染前先缩放。在转换为ANSI之前,使用将图像调整到终端尺寸。
scaleBitmap - 使用图像组件处理GIF动图。不要手动循环帧,组件会处理动画计时。
- 视频播放需要外部播放器。会检查mpv或mplayer是否存在。如果两者都未找到,视频功能将无法使用。
detectVideoPlayer() - W3M覆盖层需要w3mimgdisplay。这是w3m的独立二进制文件。
- 清理图像缓存。调用或
clearImageCache(eid)释放内存。clearAllImageCaches() - 使用子路径导入实现树摇优化。从导入而非
@blecsd/media/gif,仅引入所需功能。@blecsd/media