release: v0.2.5
CI / fmt (rustfmt --check) (push) Waiting to run
CI / clippy (ubuntu-latest) (push) Waiting to run
CI / clippy (windows-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run
CI / build release (ubuntu-latest) (push) Waiting to run
CI / build release (windows-latest) (push) Waiting to run
CI / runtime smoke (ubuntu x64, xvfb) (push) Waiting to run
CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / update-e2e (windows x64) (push) Waiting to run
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Waiting to run
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Waiting to run
Guards / core 零 UI 守门 (push) Waiting to run
Guards / presenter 零 UI 守门 (push) Waiting to run
Guards / design 零第三方依赖守门 (push) Waiting to run
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Release guard / 发布标记语义守门(push tag → 稳定版) (push) Waiting to run
Signing status(播报,不是门禁) / 判据自测(三行/退出码/变异重放/接线) (push) Waiting to run
Signing status(播报,不是门禁) / 签名就绪度(播报) (push) Waiting to run
Release / 选槽位(默认四个:Windows 两档 + Linux 两档) (push) Waiting to run
CI / fmt (rustfmt --check) (push) Waiting to run
CI / clippy (ubuntu-latest) (push) Waiting to run
CI / clippy (windows-latest) (push) Waiting to run
CI / test (ubuntu-latest) (push) Waiting to run
CI / test (windows-latest) (push) Waiting to run
CI / build release (ubuntu-latest) (push) Waiting to run
CI / build release (windows-latest) (push) Waiting to run
CI / runtime smoke (ubuntu x64, xvfb) (push) Waiting to run
CI / runtime smoke (macos arm64, probe) (push) Skipped
CI / update-e2e (windows x64) (push) Waiting to run
GPU evidence / GPU 判据自测(不需 GPU;跳过 ≠ 已验证) (push) Waiting to run
GPU evidence / GPU 证据 — 探测可用产物与硬件适配器 (push) Waiting to run
Guards / core 零 UI 守门 (push) Waiting to run
Guards / presenter 零 UI 守门 (push) Waiting to run
Guards / design 零第三方依赖守门 (push) Waiting to run
Guards / unsafe 落点守门(集合必须恰好等于期望) (push) Waiting to run
Guards / 文件行数 ≤400 守门 (push) Waiting to run
Guards / 依赖许可与来源(cargo-deny) (push) Waiting to run
Release guard / 发布标记语义守门(push tag → 稳定版) (push) Waiting to run
Signing status(播报,不是门禁) / 判据自测(三行/退出码/变异重放/接线) (push) Waiting to run
Signing status(播报,不是门禁) / 签名就绪度(播报) (push) Waiting to run
Release / 选槽位(默认四个:Windows 两档 + Linux 两档) (push) Waiting to run
This commit is contained in:
commit
0522a41a52
1587 files changed
+323674
No files matched your search
@@ -0,0 +1,88 @@
|
||||
//! 一张截图的**预览**(M251 · SAVESHOTS,D54 第 2/6 条)。
|
||||
//!
|
||||
//! ## 像素为什么走 DTO
|
||||
//!
|
||||
//! D54 定「**不落盘缩略图**」:写 PNG 要编码器,而编码器只为一份磁盘缓存引入不划算。于是像素
|
||||
//! 就直接躺在快照里跟着 IPC 面走,由壳层用 `iced::widget::image::Handle::from_rgba` 建句柄。
|
||||
//! 代价是快照变大(60 张 × 160² × 4B ≈ 6MB),收益是零编码、零临时文件、零清理。
|
||||
//!
|
||||
//! ## 三种状态,一种都不许静默
|
||||
//!
|
||||
//! | 状态 | 什么时候 | 界面 |
|
||||
//! |---|---|---|
|
||||
//! | [`ScreenshotPreview::Ready`] | 解出来了 | 缩略图 |
|
||||
//! | [`ScreenshotPreview::NotGenerated`] | 本次刷新超出 60 张上限(按 mtime 倒序只解前 60) | 「未生成预览」占位 |
|
||||
//! | [`ScreenshotPreview::Failed`] | 超过 8 MiB / 读不出来 / 解不出来 | 占位 + **逐条**原因 |
|
||||
//!
|
||||
//! **不拼人读文案**(M161):这里只有结构与 [`ScreenshotThumbKind`],句子由 app 层按 i18n key 现求。
|
||||
//!
|
||||
//! ## 为什么失败也带 `detail`
|
||||
//!
|
||||
//! 「读不出来」有五种完全不同的原因(权限 / 被占用 / 文件已删 / 是目录 / 盘掉了),用户能自己
|
||||
//! 处理的那几种全靠这句话。原文只进括号,不进主句(与 [`super::SavesFailureView`] 同一条口径)。
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
/// 一张截图的预览状态。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ScreenshotPreview {
|
||||
/// 还没生成:本次刷新超出上限(按 mtime 倒序只解前 60 张,上限常量在
|
||||
/// `crate::host::saves::thumbs::MAX_PER_REFRESH`——策略住在解码那一侧,DTO 只表达状态)。
|
||||
NotGenerated,
|
||||
/// 解出来了(RGBA8,行主序,`width × height × 4`)。
|
||||
Ready(ThumbnailView),
|
||||
/// 没解出来(逐条可见的原因 + 原文)。
|
||||
Failed(ScreenshotThumbFailure),
|
||||
}
|
||||
|
||||
impl ScreenshotPreview {
|
||||
/// 这一拍有没有可画的像素。
|
||||
#[must_use]
|
||||
pub fn thumbnail(&self) -> Option<&ThumbnailView> {
|
||||
match self {
|
||||
Self::Ready(view) => Some(view),
|
||||
Self::NotGenerated | Self::Failed(_) => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// 一张缩略图的像素(**未落盘**,见模块文档)。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ThumbnailView {
|
||||
/// 宽(像素)。
|
||||
pub width: u32,
|
||||
/// 高(像素)。
|
||||
pub height: u32,
|
||||
/// RGBA8 字节,行主序,长度必须是 `width × height × 4`。
|
||||
pub pixels: Vec<u8>,
|
||||
}
|
||||
|
||||
impl ThumbnailView {
|
||||
/// 像素数与宽高对得上吗(壳层建 `Handle` 之前**必须**成立:`from_rgba` 不校验长度,
|
||||
/// 长度不对就是一张花屏图或者一次 panic 后的空窗)。
|
||||
#[must_use]
|
||||
pub fn is_consistent(&self) -> bool {
|
||||
let expected = u64::from(self.width) * u64::from(self.height) * 4;
|
||||
expected == self.pixels.len() as u64
|
||||
}
|
||||
}
|
||||
|
||||
/// 一张截图的预览为什么没出来(**每一种在 UI 上都有一句话**)。
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub struct ScreenshotThumbFailure {
|
||||
/// 大类。
|
||||
pub kind: ScreenshotThumbKind,
|
||||
/// 底层原文(OS 错误 / 解码器原文);只进括号,不进主句。
|
||||
pub detail: String,
|
||||
}
|
||||
|
||||
/// 预览失败的大类。
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
pub enum ScreenshotThumbKind {
|
||||
/// 单张超过解码上限(8 MiB):**不解码**,直接跳过(D54 第 5 条)。
|
||||
TooLarge,
|
||||
/// 读不出来(不存在 / 无权限 / 被占用)。
|
||||
Unreadable,
|
||||
/// 读到了但解不出来(坏 PNG / 不支持的格式)。
|
||||
Decode,
|
||||
}
|
||||
Reference in new issue
Block a user