1 /*
2 ** SPDX-License-Identifier: BSD-3-Clause
3 ** Copyright Contributors to the OpenEXR Project.
4 */
5
6 #include "internal_compress.h"
7 #include "internal_decompress.h"
8
9 #include "internal_coding.h"
10
11 #include <stdio.h>
12 #include <string.h>
13
14 #define MIN_RUN_LENGTH 3
15 #define MAX_RUN_LENGTH 127
16
17 uint64_t
internal_rle_compress(void * out,uint64_t outbytes,const void * src,uint64_t srcbytes)18 internal_rle_compress (
19 void* out, uint64_t outbytes, const void* src, uint64_t srcbytes)
20 {
21 int8_t* cbuf = out;
22 const int8_t* runs = src;
23 const int8_t* end = runs + srcbytes;
24 const int8_t* rune = runs + 1;
25 uint64_t outb = 0;
26
27 while (runs < end)
28 {
29 uint8_t curcount = 0;
30 while (rune < end && *runs == *rune && curcount < MAX_RUN_LENGTH)
31 {
32 ++rune;
33 ++curcount;
34 }
35
36 if (curcount >= (MIN_RUN_LENGTH - 1))
37 {
38 cbuf[outb++] = (int8_t) curcount;
39 cbuf[outb++] = *runs;
40
41 runs = rune;
42 }
43 else
44 {
45 /* uncompressable */
46 ++curcount;
47 while (rune < end &&
48 ((rune + 1 >= end || *rune != *(rune + 1)) ||
49 (rune + 2 >= end || *(rune + 1) != *(rune + 2))) &&
50 curcount < MAX_RUN_LENGTH)
51 {
52 ++curcount;
53 ++rune;
54 }
55 cbuf[outb++] = (int8_t) (-((int) curcount));
56 while (runs < rune)
57 cbuf[outb++] = *runs++;
58 }
59 ++rune;
60 if (outb >= outbytes) break;
61 }
62 return outb;
63 }
64
65 /**************************************/
66
67 static void
reorder_and_predict(void * scratch,const void * packed,uint64_t packedbytes)68 reorder_and_predict (void* scratch, const void* packed, uint64_t packedbytes)
69 {
70 int8_t* t1 = scratch;
71 int8_t* t2 = t1 + (packedbytes + 1) / 2;
72 const int8_t* in = packed;
73 const int8_t* stop = in + packedbytes;
74 while (in < stop)
75 {
76 *(t1++) = *(in++);
77 if (in < stop) *(t2++) = *(in++);
78 }
79
80 t1 = scratch;
81 stop = t1 + packedbytes;
82 int p = *(t1++);
83 while (t1 < stop)
84 {
85 int d = (int) (*t1) - p + (128 + 256);
86 p = *t1;
87 *t1++ = (int8_t) (d);
88 }
89 }
90
91 exr_result_t
internal_exr_apply_rle(exr_encode_pipeline_t * encode)92 internal_exr_apply_rle (exr_encode_pipeline_t* encode)
93 {
94 exr_result_t rv;
95 uint64_t outb, srcb;
96
97 srcb = encode->packed_bytes;
98
99 rv = internal_encode_alloc_buffer (
100 encode,
101 EXR_TRANSCODE_BUFFER_SCRATCH1,
102 &(encode->scratch_buffer_1),
103 &(encode->scratch_alloc_size_1),
104 srcb);
105 if (rv != EXR_ERR_SUCCESS) return rv;
106
107 reorder_and_predict (encode->scratch_buffer_1, encode->packed_buffer, srcb);
108
109 outb = internal_rle_compress (
110 encode->compressed_buffer,
111 encode->compressed_alloc_size,
112 encode->scratch_buffer_1,
113 srcb);
114
115 if (outb >= srcb)
116 {
117 memcpy (encode->compressed_buffer, encode->packed_buffer, srcb);
118 outb = srcb;
119 }
120 encode->compressed_bytes = outb;
121 return EXR_ERR_SUCCESS;
122 }
123
124 /**************************************/
125
126 uint64_t
internal_rle_decompress(uint8_t * out,uint64_t outsz,const uint8_t * src,uint64_t packsz)127 internal_rle_decompress (
128 uint8_t* out, uint64_t outsz, const uint8_t* src, uint64_t packsz)
129 {
130 const int8_t* in = (const int8_t*) src;
131 uint8_t* dst = (uint8_t*) out;
132 uint64_t unpackbytes = 0;
133 uint64_t outbytes = 0;
134
135 while (unpackbytes < packsz)
136 {
137 if (*in < 0)
138 {
139 uint64_t count = (uint64_t) (-((int) *in++));
140 ++unpackbytes;
141 if (unpackbytes + count > packsz) return EXR_ERR_CORRUPT_CHUNK;
142 if (outbytes + count > outsz) return EXR_ERR_CORRUPT_CHUNK;
143
144 memcpy (dst, in, count);
145 in += count;
146 dst += count;
147 unpackbytes += count;
148 outbytes += count;
149 }
150 else
151 {
152 uint64_t count = (uint64_t) (*in++);
153 if (unpackbytes + 2 > packsz) return EXR_ERR_CORRUPT_CHUNK;
154 unpackbytes += 2;
155
156 ++count;
157 if (outbytes + count > outsz) return EXR_ERR_CORRUPT_CHUNK;
158
159 memset (dst, *(const uint8_t*) in, count);
160 dst += count;
161 outbytes += count;
162 ++in;
163 }
164 }
165 return outbytes;
166 }
167
168 static void
unpredict_and_reorder(void * out,void * scratch,uint64_t packedbytes)169 unpredict_and_reorder (void* out, void* scratch, uint64_t packedbytes)
170 {
171 int8_t* t1 = scratch;
172 int8_t* t2 = t1 + (packedbytes + 1) / 2;
173 int8_t* s = out;
174 const int8_t* stop = t1 + packedbytes;
175
176 ++t1;
177 while (t1 < stop)
178 {
179 int d = (int) (t1[-1]) + (int) (t1[0]) - 128;
180 t1[0] = (int8_t)d;
181 ++t1;
182 }
183
184 t1 = scratch;
185 stop = s + packedbytes;
186 while (s < stop)
187 {
188 *(s++) = *(t1++);
189 if (s < stop) *(s++) = *(t2++);
190 }
191 }
192
193 exr_result_t
internal_exr_undo_rle(exr_decode_pipeline_t * decode,const void * src,uint64_t packsz,void * out,uint64_t outsz)194 internal_exr_undo_rle (
195 exr_decode_pipeline_t* decode,
196 const void* src,
197 uint64_t packsz,
198 void* out,
199 uint64_t outsz)
200 {
201 exr_result_t rv;
202 uint64_t unpackb;
203 rv = internal_decode_alloc_buffer (
204 decode,
205 EXR_TRANSCODE_BUFFER_SCRATCH1,
206 &(decode->scratch_buffer_1),
207 &(decode->scratch_alloc_size_1),
208 outsz);
209 if (rv != EXR_ERR_SUCCESS) return rv;
210
211 unpackb =
212 internal_rle_decompress (decode->scratch_buffer_1, outsz, src, packsz);
213 if (unpackb != outsz) return EXR_ERR_CORRUPT_CHUNK;
214
215 unpredict_and_reorder (out, decode->scratch_buffer_1, outsz);
216 return EXR_ERR_SUCCESS;
217 }
218