Snipaste / feedback

Feedback & wiki for Snipaste https://snipaste.com
3.06k stars 197 forks source link

在 Windows 版本中加入 ICC 的支持,适配广色域显示器 #1520

Open chemharuka opened 4 years ago

chemharuka commented 4 years ago

在正确校色的显示器下(这个时候会有一个显示器的icc文件)进行截图并输出为文件,使得文件包含屏幕的icc配置文件。 这样一来使用专业软件打开截图的话,可以正确地还原色彩。 icc/icm文件可以让用户手动提供,不过通常校色出来的文件大小约接近1MB,将其嵌入图片会使得大小显著增加。所以正确的办法是使用argyll库,对图片进行色彩空间转换。argyll库的cctiff.exe可以提供图片之间的色彩转换。(argyll库采用的是AGLP许可,因此商业使用也是可行的) https://www.argyllcms.com/doc/cctiff.html 而srgb的icm在displaycal中带有,而且网上可以方便地找到。总之,只要用户提供自己显示器的icm文件,每次保存图片snipaste都可以将其转化为标准srgb色域下的图片。

以snipaste产生的截图 input.jpg为例,首先用imagemagick将其转化为tiff(我并不知道snipaste会如何保留原始数据): linux@wsl: ~$ convert input.jpg input.tiff 之后用cctiff.exe转换: PS C:\xxx> .\cctiff.exe -ip display.icm -i sRGB.icc color.tiff output.tiff 得到的tiff再转化为最终所要的文件: linux@wsl: ~$ convert output.tiff output.png 最终我获得了一个sRGB空间下的png文件,其颜色以感知的形式从高色域屏的色彩空间转化为了sRGB空间中。

上述方法的某些问题:cctiff识别tiff文件不太全面,jpg用imagemagick转化得到的tiff可以正确识别,但png就不行,可能是某些参数需要设置好。但总之这是最方便的解决高色域屏截图出正确颜色的方法了。 output 左为显示器显示的颜色的大致样貌(饱和度更大),右为如果不进行转换,截图产生的颜色。

liulex commented 4 years ago

感谢指导,我有空研究下!

wdhwg001 commented 4 years ago

我这边建议提供选项“不包含”、“嵌入”和“转换”三者,因为在网页设计而不是印刷设计的领域,保证颜色代码的稳定性十分关键,而平面设计领域则可能需要反复比对转换前后的色彩。

lzy0702 commented 4 years ago

同样地,现在的各种截图工具对HDR界面的程序截图都不正常,应该也加入一个选项,将HDR映射到SDR或者在截图中附加HDR的metadata

wangzishi commented 4 years ago

同样地,现在的各种截图工具对HDR界面的程序截图都不正常,应该也加入一个选项,将HDR映射到SDR或者在截图中附加HDR的metadata

可以直接使用支持 hdr 的图片格式吗?比如 jxr 或者 avif、webp 什么之类?

我目前解决 hdr 下截图是通过在使用 Win + Alt + PrtScn 用 Xbox Game Bar 的捕获截图,会同时生成一张 hdr 的 jxr 截图与 sdr 的 png 截图。

fuweichin commented 1 year ago

“截屏时嵌入ICC Profile”, 这功能macOS, iOS, Android早已支持,虽然Android截图程序对此有额外条件/缺陷:1.要求全屏、2.裁剪后会导致ICC丢失。

Windows的截图程序是时候跟进了。

致开发者 如何在UWP应用程序中获得当前视图的ICC Profile

using Windows.Graphics.Display;

// ...

private async void btnSaveICCProfile_Click(object sender, RoutedEventArgs e)
{
    DisplayInformation displayInfo = DisplayInformation.GetForCurrentView();
    // save ICC profile
    FileSavePicker picker = new FileSavePicker();
    picker.SuggestedStartLocation = PickerLocationId.Downloads;
    picker.FileTypeChoices.Add("application/vnd.iccprofile", new List<string>() { ".icc" });
    picker.SuggestedFileName = "display";
    StorageFile file = await picker.PickSaveFileAsync();

    using (IRandomAccessStream readableStream = await displayInfo.GetColorProfileAsync())
    using (Stream inputStream = readableStream.AsStreamForRead())
    using (Stream outputStream = await file.OpenStreamForWriteAsync())
    {
        Console.WriteLine("Writing file " + file.Name);
        inputStream.CopyTo(outputStream);
        Console.WriteLine("Saved as " + file.Name);
    }
}

private void btnShowDisplayInfo_Click(object sender, RoutedEventArgs e)
{
    DisplayInformation displayInfo = DisplayInformation.GetForCurrentView();
    // show advanced color info
    AdvancedColorInfo colorInfo = displayInfo.GetAdvancedColorInfo();
    StringBuilder sb = new StringBuilder();
    bool wgc = colorInfo.IsAdvancedColorKindAvailable(AdvancedColorKind.WideColorGamut);
    sb.Append("WideColorGamut: ").Append(wgc).Append("\n");
    sb.Append("RedPrimary: ").Append(colorInfo.RedPrimary.ToString()).Append("\n");
    sb.Append("GreenPrimary: ").Append(colorInfo.GreenPrimary.ToString()).Append("\n");
    sb.Append("BluePrimary: ").Append(colorInfo.BluePrimary.ToString()).Append("\n");
    sb.Append("WhitePoint: ").Append(colorInfo.WhitePoint.ToString()).Append("\n");
    sb.Append("\n");
    bool hdr = colorInfo.IsAdvancedColorKindAvailable(AdvancedColorKind.HighDynamicRange);
    sb.Append("HighDynamicRange: ").Append(hdr).Append("\n");
    if (hdr)
    {
        sb.Append("MaxAverageFullFrameLuminanceInNits: ").Append(colorInfo.MaxAverageFullFrameLuminanceInNits).Append("\n");
        sb.Append("MaxLuminanceInNits: ").Append(colorInfo.MaxLuminanceInNits).Append("\n");
    }
    sb.Append("\n");
    sb.Append("DPR: ").Append(displayInfo.RawPixelsPerViewPixel).Append("\n");
    sb.Append("\n");
    txtDisplayInfo.Text = sb.ToString();
}

如何在PNG中嵌入ICC profile 添加iCCP chunk, 设置chunk name为例如"Snipaste", ...

如何在JPEG中嵌入ICC profile 添加APP2 segment, 设置segment identifier 为"ICC_PROFILE\x00", ...

如何在BMP中嵌入ICC profile 使用BITMAPV5HEADER (124字节), 设置colorSpaceType为"MBED", ...

P.S. Windows用户在使用广色域显示器时,需要在显示器端/Windows端同步指定颜色模式/ICC profile(除非有某个应用程序自动来做这件事)。见 You're Using Your Monitor WRONG! (Here's How to Fix It) 的解释。

fuweichin commented 6 months ago

经常要用但总是缺,我忍在不住再多说几句。目前的变通解决方法有

方法1: 【推荐】使用TweakPNG 先使用TweakPNG从一个内嵌有指定ICC Profile有的正常PNG中导出iCCP chunk,之后对于需要处理的PNG,插入iCCP chunk到PNG image header后即可(同时删除sRGB、gMMa等颜色相关的chunks,如果存在)。

方法2: 使用Exif Tools之类的命令行工具 通过命令行嵌入icc到图片中,如

exiftool "-icc_profile<=profile.icc" photo.jpg

方法3: 使用Photoshop 先在安装所需嵌入icc文件到Windows系统,再运行Photoshop打开图片,通过菜单 Edit / Assign Profile选择一个ICC Profile