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
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
use super::encoder::{ArithEncoder, encode_bit_raw};
/// TPGD(Typical Prediction for Generic Direct coding)用コンテキスト番号。
///
/// C++版の `TPGDCTX = 0x9b25` に対応。
const TPGD_CTX: usize = 0x9b25;
impl ArithEncoder {
/// パックド1bpp画像を算術符号化する。
///
/// C++版 `jbig2enc_bitimage()` に対応。
/// Leptonicaの1bppパックドフォーマット(各行は32ビットワード単位)を想定。
///
/// Template 0、デフォルトAT位置を使用。
/// コンテキストビット構成: c1(5bit, y-2行) | c2(7bit, y-1行) | c3(4bit, 現在行)
///
/// `data`: パックド1bppビット列(`u32`スライスとして渡す)
/// `mx`: 画像幅(ピクセル)
/// `my`: 画像高さ(ピクセル)
/// `duplicate_line_removal`: trueのときTPGDを使用
///
/// *各行の末尾パッドビットはゼロでなければならない*
pub fn encode_bitimage(
&mut self,
data: &[u32],
mx: u32,
my: u32,
duplicate_line_removal: bool,
) {
let words_per_row = mx.div_ceil(32) as usize;
let mut ltp: u8 = 0;
let mut sltp: u8 = 0;
for y in 0..my as usize {
// コンテキスト用ワード(y-2行、y-1行、現在行)
let mut w1: u32 = if y >= 2 {
data[(y - 2) * words_per_row]
} else {
0
};
let mut w2: u32 = if y >= 1 {
data[(y - 1) * words_per_row]
} else {
0
};
if y >= 1 && duplicate_line_removal {
// 前行と現在行が同じかチェック
let same = data[y * words_per_row..y * words_per_row + words_per_row]
== data[(y - 1) * words_per_row..(y - 1) * words_per_row + words_per_row];
if same {
sltp = ltp ^ 1;
ltp = 1;
} else {
sltp = ltp;
ltp = 0;
}
}
if duplicate_line_removal {
let ctx = &mut self.context;
encode_bit_raw(
&mut self.c,
&mut self.a,
&mut self.ct,
&mut self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
ctx,
TPGD_CTX,
sltp,
);
if ltp != 0 {
continue;
}
}
let mut w3: u32 = data[y * words_per_row];
// 初期コンテキストビット(各行の最初のピクセル前に先読みしてある分)
// c1: y-2行目から3ビット(上から3列)
// c2: y-1行目から4ビット(上から4列)
let mut c1: u16 = (w1 >> 29) as u16; // 3ビット
let mut c2: u16 = (w2 >> 28) as u16; // 4ビット
let mut c3: u16 = 0; // 現在行の過去4ビット
// 使用済みビットをシフトアウト
w1 <<= 3;
w2 <<= 4;
for x in 0..mx as usize {
let tval = ((c1 << 11) | (c2 << 4) | c3) as usize;
let v = ((w3 & 0x8000_0000) >> 31) as u8;
{
let ctx = &mut self.context;
encode_bit_raw(
&mut self.c,
&mut self.a,
&mut self.ct,
&mut self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
ctx,
tval,
v,
);
}
// コンテキストビットをシフトし、w1/w2のMSBを取得
// C++版と同じ順序: ① MSB取得 → ② シフト/リロード
c1 = (c1 << 1) | ((w1 >> 31) as u16);
c2 = (c2 << 1) | ((w2 >> 31) as u16);
c3 = (c3 << 1) | v as u16;
let m = x % 32;
// w1: y-2行目ワードのロールイン(28ビット目でリロード)
if m == 28 && y >= 2 {
let wordno = x / 32 + 1;
w1 = if wordno < words_per_row {
data[(y - 2) * words_per_row + wordno]
} else {
0
};
} else {
w1 = w1.wrapping_shl(1);
}
// w2: y-1行目ワードのロールイン(27ビット目でリロード)
if m == 27 && y >= 1 {
let wordno = x / 32 + 1;
w2 = if wordno < words_per_row {
data[(y - 1) * words_per_row + wordno]
} else {
0
};
} else {
w2 = w2.wrapping_shl(1);
}
// w3: 現在行ワードのロールイン(31ビット目でリロード)
if m == 31 {
let wordno = x / 32 + 1;
w3 = if wordno < words_per_row {
data[y * words_per_row + wordno]
} else {
0
};
} else {
w3 <<= 1;
}
// マスクはシフト・リロード後に適用(C++版と同じ)
c1 &= 0x1f;
c2 &= 0x7f;
c3 &= 0x0f;
}
}
}
/// リファインメント符号化(2画像間の差分を符号化)。
///
/// C++版 `jbig2enc_refine()` に対応。
/// テンプレート画像からターゲット画像への差分を13ピクセルテンプレートで符号化。
///
/// コンテキストビット構成:
/// c1(3bit, templ y-1行) | c2(3bit, templ y行) | c3(3bit, templ y+1行) | c4(3bit, target y-1行) | c5(1bit, 現在ピクセルの左)
///
/// `ox` は -1, 0, 1 のみ有効。
#[allow(clippy::too_many_arguments)]
pub fn encode_refine(
&mut self,
templ: &[u32],
tx: u32,
ty: u32,
target: &[u32],
mx: u32,
my: u32,
ox: i32,
oy: i32,
) {
let templwords_per_row = tx.div_ceil(32) as usize;
let words_per_row = mx.div_ceil(32) as usize;
for y in 0..my as usize {
let temply = y as i32 + oy;
let mut w1: u32 = if temply >= 1 && temply - 1 < ty as i32 {
templ[(temply as usize - 1) * templwords_per_row]
} else {
0
};
let mut w2: u32 = if temply >= 0 && temply < ty as i32 {
templ[temply as usize * templwords_per_row]
} else {
0
};
let mut w3: u32 = if temply >= -1 && temply + 1 < ty as i32 {
templ[(temply + 1) as usize * templwords_per_row]
} else {
0
};
let mut w4: u32 = if y >= 1 {
target[(y - 1) * words_per_row]
} else {
0
};
let mut w5: u32 = target[y * words_per_row];
let shiftoffset = (30 + ox) as u32;
let mut c1: u16 = (w1 >> shiftoffset) as u16 & 3;
let mut c2: u16 = (w2 >> shiftoffset) as u16 & 3;
let mut c3: u16 = (w3 >> shiftoffset) as u16 & 3;
let mut c4: u16 = (w4 >> 30) as u16 & 3;
let mut c5: u16 = 0;
let bits_to_trim = (2 - ox) as u32;
w1 <<= bits_to_trim;
w2 <<= bits_to_trim;
w3 <<= bits_to_trim;
w4 <<= 2;
for x in 0..mx as usize {
let tval = ((c1 << 10) | (c2 << 7) | (c3 << 4) | (c4 << 1) | c5) as usize;
let v = (w5 >> 31) as u8;
{
let ctx = &mut self.context;
encode_bit_raw(
&mut self.c,
&mut self.a,
&mut self.ct,
&mut self.b,
&mut self.bp,
&mut self.output_chunks,
&mut self.outbuf,
ctx,
tval,
v,
);
}
c1 = ((c1 << 1) | (w1 >> 31) as u16) & 7;
c2 = ((c2 << 1) | (w2 >> 31) as u16) & 7;
c3 = ((c3 << 1) | (w3 >> 31) as u16) & 7;
c4 = ((c4 << 1) | (w4 >> 31) as u16) & 7;
c5 = v as u16;
let m = x % 32;
let wordno = x / 32 + 1;
// テンプレートワードのロールイン
if m == (29 + ox) as usize {
w1 = if wordno < templwords_per_row && temply >= 1 && temply - 1 < ty as i32 {
templ[(temply as usize - 1) * templwords_per_row + wordno]
} else {
0
};
w2 = if wordno < templwords_per_row && temply >= 0 && temply < ty as i32 {
templ[temply as usize * templwords_per_row + wordno]
} else {
0
};
w3 = if wordno < templwords_per_row && temply >= -1 && temply + 1 < ty as i32 {
templ[(temply + 1) as usize * templwords_per_row + wordno]
} else {
0
};
} else {
w1 <<= 1;
w2 <<= 1;
w3 <<= 1;
}
// ターゲット前行ワードのロールイン
if m == 29 && y >= 1 {
w4 = if wordno < words_per_row {
target[(y - 1) * words_per_row + wordno]
} else {
0
};
} else {
w4 <<= 1;
}
// 現在行ワードのロールイン
if m == 31 {
w5 = if wordno < words_per_row {
target[y * words_per_row + wordno]
} else {
0
};
} else {
w5 <<= 1;
}
}
}
}
}