图像格式转换之 Jpeg2Jxr

为什么转?因为 JXR 格式在同等质量的情况下,存储空间比 JPEG 节约了 45-50%。

之前在《从 Windows 8 新功能推理某产品的八哥》提到过现在手机上的省流量 App,其原理就是压缩图片,但为了提高效果,这个压缩基本都是有损的,流量减少了,但是图片质量下降了,有的下降可以忍受,有的则令人发指!比如,长微博,文字转图片,这种图片线条分明,相邻像素值对比可能很大(黑白分明),这类图片采用高压缩比的 JPEG 压缩后,图片质量往往很差。

再举个例子:QR 码图片,您可以做一下试验,为了说明 JPEG 不适合存储线条型图片,哥采用一张蛋疼的 1290*1290 像素的 QR 码图片,保存为 JPEG 大小是 4.76MB,但保存为 PNG 格式时只有 52.4KB,请注意单位,前者是后者大小的将近 100 倍!!

大家可能比较少关注 WP,也许您没听过 DataSense,简单地说,它就是微软做的节省流量的 App。号称可以节约 45% 的流量,这么大的压缩率,除了优化 HTML 相关的文本之外,对图片的压缩肯定是必须的!推测 DataSense 可能使用了 JPEG XR 格式来转化其他格式的图片。

JPEG XR 虽然已经成为一种标准,但目前依然只有微软支持,所以,如果您想把这个技术应用到 iOS、Android 的节省流量 App 中,那很抱歉,此路暂时还不通。

根据实测,IE9@PC、IE10@PC、IE10@WP8 都是支持 JXR 格式的。下面是用 C++/CLI 写的很简单的一个格式转化程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using namespace System;
using namespace System::IO;
using namespace System::Windows::Media;
using namespace System::Windows::Media::Imaging;

bool ConvertToJxr(System::String^ source_name)
{

//try {
Stream^ stream = gcnew FileStream(source_name, FileMode::Open, FileAccess::Read, FileShare::Read);
BitmapDecoder^ jpeg_decoder = BitmapDecoder::Create(stream, BitmapCreateOptions::PreservePixelFormat, BitmapCacheOption::None);
//JpegBitmapDecoder^ jpeg_decoder = gcnew JpegBitmapDecoder(gcnew Uri(source_name, UriKind::RelativeOrAbsolute), BitmapCreateOptions::PreservePixelFormat, BitmapCacheOption::None);
//Console::WriteLine(L"Author: `{0}'", jpeg_decoder->Metadata->Title);
FileStream^ jxr_file_stream = gcnew FileStream(source_name + L".jxr", FileMode::Create);
WmpBitmapEncoder^ jxr_encoder = gcnew WmpBitmapEncoder;
//BitmapMetadata^ metadata = gcnew BitmapMetadata(L"wmphoto");

for each (BitmapFrame ^ frame in jpeg_decoder->Frames) {
jxr_encoder->Frames->Add(BitmapFrame::Create(frame, jpeg_decoder->Thumbnail, (BitmapMetadata^)frame->Metadata, jpeg_decoder->ColorContexts));
}
//jxr_encoder->Metadata = metadata;
jxr_encoder->Save(jxr_file_stream);
//} catch (...) {
// return false;
//}
return true;
}

int main(array<System::String ^> ^args)
{

for each (auto arg in args) {
if (File::Exists(arg)) {
if (ConvertToJxr(arg)) {
Console::WriteLine(L"Converted: `{0}'", arg);
}
} else {
Console::WriteLine(L"NOT Exists: `{0}'", arg);
}
}
return 0;
}

文末是一些搜索到的关于 JPEG XR 的资料,可供参考:

http://jpeg.org/newsrel26.html

JPEG XR (ISO/IEC 29199-2) is now an International Standard and also an ITU-T Recommendation (T.832).

JPEG XR(旧称 HD Photo 及 Windows Media Photo)是一种连续色调静止图像压缩算法和文件格式,由Microsoft开发,属于Windows Media家族的一部分。它支持有损数据压缩以及无损数据压缩,并且是微软的XPS文档的首选图像格式。目前支持的软件包括.NET Framework(3.0 or newer),Windows Vista/Windows 7、Internet Explorer 9,Flashplayer 11等。

JPEG XR(微软HD Photo格式)2009 年,成为 ITU-T 推荐的国际标准(ISO/IEC 29199-2)。JPEG XR 的标准化确保数码相机、打印机、显示器和软件公司能够在开发其新产品的时候兼容互通。其核心技术由微软核心媒体开发团队开发完成,针对当前和将来的数字图像发展需求以提供了许多新的优势和特点。

在 Vista 操作系统中已经支持了这种新的文件格式,JPEG XR 相比其它技术更有优势,其中包括更好的压缩技术,以一半的文件大小保存与 JPEG 相同质量的图像,或以相同大小的文件保存质量相当于 JPEG 两倍的图像。JPEG 组织还对微软开放与 JPEG XR 相关的专利的决策表示了赞扬,称微软免许可费政策将有助于JPEG推动 JPEG XR 普及,有助于确保它能够被更多的用户所采用。JPEG 组织还鼓励其它公司向微软学习。

mif2png(QQGame 专用 mif 格式转 png 格式)

2011-11-26 00:27 发布于百度空间,由于百度空间停运,搬到此处。

大学时代的作品《UMU 游戏之争上游》的副产品,mif2bmp 改进版,用 GdiPlus 来产生 png 格式图片。

mif2png.exe 下载:http://download.csdn.net/detail/umu/3843545

以前 UMU 有写过文章分析 mif 格式,不过很早了,懒得找,直接上代码吧,先看头部结构体:

1
2
3
4
5
6
7
8
9
10
#pragma pack(1)
struct MifHeader
{
DWORD version;
DWORD width;
DWORD height;
DWORD type;
DWORD frame_count;
};
#pragma pack()

以下代码是 C# 写的 Paint.NET 文件类型插件 MifFileType.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
using System;
using System.Collections.Generic;
using System.Text;
using PaintDotNet;
using PaintDotNet.Data;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using System.Drawing.Imaging;

namespace MifFileType
{
public class MifFileType : FileType
{
public MifFileType()
: base("MIF Files", FileTypeFlags.SupportsLoading | FileTypeFlags.SupportsLayers, new String[] { ".mif" })
{

}

protected override Document OnLoad(Stream input)
{

if (input.Length < 20)
{
MessageBox.Show("Invalid MIF File", "UMU Corporation - MifFileTypePlugIn", MessageBoxButtons.OK, MessageBoxIcon.Error);

Bitmap b = new Bitmap(800, 600);
return Document.FromImage(b);
}

try
{
BinaryReader br = new BinaryReader(input);

int MifVersion = br.ReadInt32();
int FrameWidth = br.ReadInt32();
int FrameHeight = br.ReadInt32();
int MifType = br.ReadInt32();
int FrameCount = br.ReadInt32();

int ImageWidth = FrameWidth;
int ImageHeight = FrameHeight * FrameCount;

bool Valid = true;
long Prefix;

if (MifType == 3)
{
Prefix = 20;
}
else if (MifType == 7)
{

Prefix = 20 + 4 * FrameCount;
}
else
{
MessageBox.Show("Invalid MIF File", "UMU Corporation - MifFileTypePlugIn", MessageBoxButtons.OK, MessageBoxIcon.Error);

Bitmap b = new Bitmap(800, 600);
return Document.FromImage(b);
}

if (MifVersion == 0)
{
if (Prefix + ImageWidth * ImageHeight * 3 != input.Length)
{
Valid = false;
}
}
else if (MifVersion == 1)
{

if (Prefix + ImageWidth * ImageHeight * 3 > input.Length)
{
Valid = false;
}
}

if (!Valid)
{
MessageBox.Show("Invalid MIF File", "UMU Corporation - MifFileTypePlugIn", MessageBoxButtons.OK, MessageBoxIcon.Error);

Bitmap b = new Bitmap(800, 600);
return Document.FromImage(b);
}

Bitmap bmp = new Bitmap(ImageWidth, ImageHeight);

for (int CurrentFrame = 0; CurrentFrame < FrameCount; ++CurrentFrame)
{
if (MifType == 7)
{
input.Seek(4, SeekOrigin.Current);
}

UInt16[] rgb16 = new UInt16[FrameWidth * FrameHeight];

for (int i = 0; i < FrameHeight * FrameWidth; ++i)
{
rgb16[i] = br.ReadUInt16();
}

Byte[] a8 = new Byte[FrameWidth * FrameHeight];

for (int i = 0; i < FrameHeight * FrameWidth; ++i)
{
a8[i] = br.ReadByte();
}

for (int y = 0; y < FrameHeight; ++y)
{
for (int x = 0; x < FrameWidth; ++x)
{
int a = a8[x + y * FrameWidth];
int r = (rgb16[x + y * FrameWidth] & 0xF800) >> 8;
int g = (rgb16[x + y * FrameWidth] & 0x07E0) >> 3;
int b = (rgb16[x + y * FrameWidth] & 0x001F) << 3;

if (a == 32)
{
a = 255;
}
else if (a > 0)
{

a <<= 3;
}

bmp.SetPixel(x, FrameHeight * CurrentFrame + y, Color.FromArgb(a, r, g, b));
}
}
}

br.Close();
return Document.FromImage(bmp);
}
catch (Exception ex)
{
MessageBox.Show(ex.Message, "UMU Corporation - MifFileTypePlugIn", MessageBoxButtons.OK, MessageBoxIcon.Warning);

Bitmap bmp = new Bitmap(800, 600);
//Document doc = Document.FromImage(bmp);
//doc.Tag = "UMU Corporation - MifFileTypePlugIn";
//return doc;
return Document.FromImage(bmp);
}
}
}

public class MifFileTypeFactory : IFileTypeFactory
{
public FileType[] GetFileTypeInstances()
{
return new FileType[] { new MifFileType() };
}
}
}