libtlg_rs/
load_tlg.rs

1use crate::stream::ReadExt;
2use crate::tvpgl::*;
3use crate::*;
4use overf::wrapping;
5use std::io::SeekFrom;
6
7fn load_tlg5<T: Read + Seek>(src: &mut T) -> Result<Tlg> {
8    let colors = src.read_u8()?;
9    let width = src.read_u32()?;
10    let height = src.read_u32()?;
11    let blockheight = src.read_u32()?;
12    let color = match colors {
13        3 => TlgColorType::Bgr24,
14        4 => TlgColorType::Bgra32,
15        1 => TlgColorType::Grayscale8,
16        _ => return Err(TlgError::UnsupportedColorType(colors)),
17    };
18    let blockcount = ((height - 1) / blockheight) + 1;
19    src.seek_relative(blockcount as i64 * 4)?;
20    let stride = width as usize * colors as usize;
21    let mut output_data = vec![0u8; width as usize * height as usize * colors as usize];
22    let mut text = [0u8; 4096];
23    let mut inbuf = vec![0u8; blockheight as usize * width as usize + 10];
24    let mut outbuf = vec![vec![0u8; blockheight as usize * width as usize + 10]; colors as usize];
25    let mut prevline: Option<Vec<u8>> = None;
26    let mut r = 0;
27    for y_blk in (0..height).step_by(blockheight as usize) {
28        for c in 0..colors {
29            let mark = src.read_u8()?;
30            let size = src.read_u32()?;
31            if mark == 0 {
32                src.read_exact(&mut inbuf[..size as usize])?;
33                r = tlg5_decompress_slide(
34                    &mut outbuf[c as usize],
35                    &inbuf[..size as usize],
36                    size as usize,
37                    &mut text,
38                    r,
39                );
40            } else {
41                src.read_exact(&mut outbuf[c as usize][..size as usize])?;
42            }
43        }
44        let y_lim = (y_blk + blockheight).min(height);
45        let mut outbufp = Vec::new();
46        for c in 0..colors {
47            outbufp.push(outbuf[c as usize].as_slice());
48        }
49        for y in y_blk..y_lim {
50            let current = &mut output_data[(y as usize * stride)..(y as usize * stride + stride)];
51            match prevline.take() {
52                Some(prev) => match color {
53                    TlgColorType::Bgr24 => {
54                        tlg5_compose_colors3(current, &prev, &outbufp, width);
55                        outbufp[0] = &outbufp[0][width as usize..];
56                        outbufp[1] = &outbufp[1][width as usize..];
57                        outbufp[2] = &outbufp[2][width as usize..];
58                    }
59                    TlgColorType::Bgra32 => {
60                        tlg5_compose_colors4(current, &prev, &outbufp, width);
61                        outbufp[0] = &outbufp[0][width as usize..];
62                        outbufp[1] = &outbufp[1][width as usize..];
63                        outbufp[2] = &outbufp[2][width as usize..];
64                        outbufp[3] = &outbufp[3][width as usize..];
65                    }
66                    TlgColorType::Grayscale8 => {
67                        tlg5_compose_colors1(current, &prev, &outbufp, width);
68                        outbufp[0] = &outbufp[0][width as usize..];
69                    }
70                },
71                None => match color {
72                    TlgColorType::Bgra32 => {
73                        let mut current_pos = 0usize;
74                        let mut pr = 0u8;
75                        let mut pg = 0u8;
76                        let mut pb = 0u8;
77                        let mut pa = 0u8;
78                        for x in 0..width as usize {
79                            let mut b = outbufp[0][x];
80                            let g = outbufp[1][x];
81                            let mut r = outbufp[2][x];
82                            let a = outbufp[3][x];
83                            wrapping! {
84                                b += g;
85                                r += g;
86                                pb += b;
87                                pg += g;
88                                pr += r;
89                                pa += a;
90                            }
91                            current[current_pos] = pb;
92                            current_pos += 1;
93                            current[current_pos] = pg;
94                            current_pos += 1;
95                            current[current_pos] = pr;
96                            current_pos += 1;
97                            current[current_pos] = pa;
98                            current_pos += 1;
99                        }
100                        outbufp[0] = &outbufp[0][width as usize..];
101                        outbufp[1] = &outbufp[1][width as usize..];
102                        outbufp[2] = &outbufp[2][width as usize..];
103                        outbufp[3] = &outbufp[3][width as usize..];
104                    }
105                    TlgColorType::Bgr24 => {
106                        let mut current_pos = 0usize;
107                        let mut pr = 0u8;
108                        let mut pg = 0u8;
109                        let mut pb = 0u8;
110                        for x in 0..width as usize {
111                            let mut b = outbufp[0][x];
112                            let g = outbufp[1][x];
113                            let mut r = outbufp[2][x];
114                            wrapping! {
115                                b += g;
116                                r += g;
117                                pb += b;
118                                pg += g;
119                                pr += r;
120                            }
121                            current[current_pos] = pb;
122                            current_pos += 1;
123                            current[current_pos] = pg;
124                            current_pos += 1;
125                            current[current_pos] = pr;
126                            current_pos += 1;
127                        }
128                        outbufp[0] = &outbufp[0][width as usize..];
129                        outbufp[1] = &outbufp[1][width as usize..];
130                        outbufp[2] = &outbufp[2][width as usize..];
131                    }
132                    TlgColorType::Grayscale8 => {
133                        let mut current_pos = 0usize;
134                        let mut pb = 0u8;
135                        for x in 0..width as usize {
136                            let b = outbufp[0][x];
137                            wrapping! {
138                                pb += b;
139                            }
140                            current[current_pos] = pb;
141                            current_pos += 1;
142                        }
143                        outbufp[0] = &outbufp[0][width as usize..];
144                    }
145                },
146            }
147            prevline = Some(current.to_vec());
148        }
149    }
150    Ok(Tlg {
151        tags: Default::default(),
152        version: 5,
153        width,
154        height,
155        color,
156        data: output_data,
157    })
158}
159
160fn load_tlg6<T: Read + Seek>(src: &mut T) -> Result<Tlg> {
161    let mut buf = [0u8; 4];
162    src.read_exact(&mut buf)?;
163    let colors = buf[0];
164    let color_type = match colors {
165        3 => TlgColorType::Bgr24,
166        4 => TlgColorType::Bgra32,
167        1 => TlgColorType::Grayscale8,
168        _ => return Err(TlgError::UnsupportedColorType(colors)),
169    };
170    if buf[1] != 0 {
171        return Err(TlgError::Str("Data flags must be 0".to_string()));
172    }
173    if buf[2] != 0 {
174        return Err(TlgError::Str("Color types must be 0".to_string()));
175    }
176    if buf[3] != 0 {
177        return Err(TlgError::Str(
178            "External golomb bit length table is not yet supported.".to_string(),
179        ));
180    }
181    let width = src.read_u32()?;
182    let height = src.read_u32()?;
183    let max_bit_length = src.read_u32()?;
184    let x_block_count = (width - 1) / (TLG6_W_BLOCK_SIZE as u32) + 1;
185    let y_block_count = (height - 1) / (TLG6_H_BLOCK_SIZE as u32) + 1;
186    let main_count = width / (TLG6_W_BLOCK_SIZE as u32);
187    let fraction = width - main_count * TLG6_W_BLOCK_SIZE as u32;
188    let mut bit_pool = vec![0u8; max_bit_length as usize / 8 + 5];
189    let mut pixelbuf = vec![0u32; width as usize * TLG6_H_BLOCK_SIZE + 1];
190    let mut filter_types = vec![0u8; x_block_count as usize * y_block_count as usize];
191    let mut lzss_text = [0u8; 4096];
192    let zero = if colors == 3 { 0xff_00_00_00u32 } else { 0 };
193    let zeroline = vec![zero; width as usize];
194    {
195        let mut p = 0;
196        let mut i = 0;
197        while i < 0x20u8 {
198            let mut j = 0;
199            while j < 0x10u8 {
200                lzss_text[p] = i;
201                p += 1;
202                lzss_text[p] = i;
203                p += 1;
204                lzss_text[p] = i;
205                p += 1;
206                lzss_text[p] = i;
207                p += 1;
208                lzss_text[p] = j;
209                p += 1;
210                lzss_text[p] = j;
211                p += 1;
212                lzss_text[p] = j;
213                p += 1;
214                lzss_text[p] = j;
215                p += 1;
216                j += 1;
217            }
218            i += 1;
219        }
220    }
221    {
222        let inbuf_size = src.read_u32()? as usize;
223        let mut inbuf = vec![0u8; inbuf_size];
224        src.read_exact(&mut inbuf)?;
225        tlg5_decompress_slide(&mut filter_types, &inbuf, inbuf_size, &mut lzss_text, 0);
226    }
227    let mut prevline = zeroline;
228    let mut outbuf = vec![0u32; width as usize * height as usize];
229    for y in (0..height).step_by(TLG6_H_BLOCK_SIZE) {
230        let y_lim = (y + TLG6_H_BLOCK_SIZE as u32).min(height);
231        let pixel_count = (y_lim - y) as usize * width as usize;
232        for c in 0..colors {
233            let mut bit_length = src.read_u32()?;
234            let method = (bit_length >> 30) & 3;
235            bit_length &= 0x3fff_ffff;
236            let byte_length = (bit_length + 7) / 8;
237            if byte_length as usize >= bit_pool.len() {
238                return Err(TlgError::Str(
239                    "Bit pool is too small for the given bit length".to_string(),
240                ));
241            }
242            src.read_exact(&mut bit_pool[..byte_length as usize])?;
243            match method {
244                0 => {
245                    tlg6_decode_golomb_values(
246                        &mut pixelbuf,
247                        pixel_count,
248                        &bit_pool,
249                        c == 0 && colors != 1,
250                        c,
251                    )?;
252                }
253                _ => return Err(TlgError::UnsupportedCompressedMethod(method as u8)),
254            }
255        }
256        let ft = &filter_types[(y as usize / TLG6_H_BLOCK_SIZE) * x_block_count as usize..];
257        let skip_bytes = (y_lim - y) as usize * TLG6_W_BLOCK_SIZE;
258        for yy in y..y_lim {
259            let curline =
260                &mut outbuf[(yy as usize * width as usize)..(yy as usize + 1) * width as usize];
261            let dir = (yy & 1) ^ 1 != 0;
262            let oddskip = ((y_lim - yy - 1) as isize) - (yy - y) as isize;
263            if main_count != 0 {
264                let start = TLG6_W_BLOCK_SIZE.min(width as usize) * (yy - y) as usize;
265                tlg6_decode_line(
266                    &prevline,
267                    curline,
268                    width,
269                    0,
270                    main_count as usize,
271                    ft,
272                    skip_bytes,
273                    &pixelbuf,
274                    start,
275                    zero,
276                    oddskip,
277                    dir,
278                )?;
279            }
280            if main_count != x_block_count {
281                let ww = TLG6_W_BLOCK_SIZE.min(fraction as usize);
282                let start = ww * (yy - y) as usize;
283                tlg6_decode_line(
284                    &prevline,
285                    curline,
286                    width,
287                    main_count as usize,
288                    x_block_count as usize,
289                    ft,
290                    skip_bytes,
291                    &pixelbuf,
292                    start,
293                    zero,
294                    oddskip,
295                    dir,
296                )?;
297            }
298            prevline = curline.to_vec();
299        }
300    }
301    let mut data = Vec::with_capacity(outbuf.len() * colors as usize);
302    for p in outbuf {
303        let t = p.to_le_bytes();
304        let b = t[0];
305        let g = t[1];
306        let r = t[2];
307        let a = t[3];
308        match color_type {
309            TlgColorType::Bgr24 => {
310                data.extend_from_slice(&[b, g, r]);
311            }
312            TlgColorType::Bgra32 => {
313                data.extend_from_slice(&[b, g, r, a]);
314            }
315            TlgColorType::Grayscale8 => {
316                data.extend_from_slice(&[b]);
317            }
318        };
319    }
320    Ok(Tlg {
321        tags: Default::default(),
322        version: 6,
323        width,
324        height,
325        color: color_type,
326        data,
327    })
328}
329
330fn internal_load_tlg<T: Read + Seek>(src: &mut T) -> Result<Tlg> {
331    let mut mark = [0; 11];
332    src.read_exact(&mut mark)?;
333    if &mark == b"TLG5.0\x00raw\x1a" {
334        load_tlg5(src)
335    } else if &mark == b"TLG6.0\x00raw\x1a" {
336        load_tlg6(src)
337    } else {
338        Err(TlgError::InvalidFormat)
339    }
340}
341
342/// Decode TLG image
343pub fn load_tlg<T: Read + Seek>(mut src: T) -> Result<Tlg> {
344    src.rewind()?;
345    let mut mark = [0; 11];
346    src.read_exact(&mut mark)?;
347    if &mark == b"TLG0.0\x00sds\x1a" {
348        let rawlen = src.read_u32()?;
349        let mut tlg = internal_load_tlg(&mut src)?;
350        let newlen = rawlen as u64 + 15;
351        src.seek(SeekFrom::Start(newlen))?;
352        let mut check = true;
353        while check {
354            let mut chunkname = [0; 4];
355            if src.read(&mut chunkname)? != 4 {
356                break;
357            }
358            let chunksize = src.read_u32()?;
359            if &chunkname == b"tags" {
360                let mut tag = vec![0; chunksize as usize];
361                src.read_exact(&mut tag)?;
362                let mut i = 0;
363                let len = tag.len();
364                while i < len {
365                    let mut namelen = 0usize;
366                    let mut c = tag[i];
367                    let mut ok = true;
368                    while c >= b'0' && c <= b'9' {
369                        namelen = namelen * 10 + (c - b'0') as usize;
370                        i += 1;
371                        if i >= len {
372                            ok = false;
373                            break;
374                        }
375                        c = tag[i];
376                    }
377                    if !ok {
378                        break;
379                    }
380                    if c != b':' {
381                        check = false;
382                        break;
383                    }
384                    i += 1;
385                    let name = tag[i..i + namelen].to_vec();
386                    i += namelen;
387                    if i >= len {
388                        break;
389                    }
390                    c = tag[i];
391                    if c != b'=' {
392                        check = false;
393                        break;
394                    }
395                    i += 1;
396                    let mut valuelen = 0usize;
397                    c = tag[i];
398                    ok = true;
399                    while c >= b'0' && c <= b'9' {
400                        valuelen = valuelen * 10 + (c - b'0') as usize;
401                        i += 1;
402                        if i >= len {
403                            ok = false;
404                            break;
405                        }
406                        c = tag[i];
407                    }
408                    if !ok {
409                        break;
410                    }
411                    if c != b':' {
412                        check = false;
413                        break;
414                    }
415                    i += 1;
416                    let value = tag[i..i + valuelen].to_vec();
417                    i += valuelen;
418                    if i >= len {
419                        check = false;
420                        break;
421                    }
422                    c = tag[i];
423                    if c != b',' {
424                        check = false;
425                        break;
426                    }
427                    i += 1;
428                    tlg.tags.insert(name, value);
429                }
430            } else {
431                // skip the chunk
432                src.seek_relative(chunksize as i64)?;
433            }
434        }
435        Ok(tlg)
436    } else {
437        src.rewind()?;
438        internal_load_tlg(&mut src)
439    }
440}