Allow incomplete scanlines in SZ mode.
[platform/upstream/libaec.git] / src / sz_compat.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include "szlib.h"
5
6 #if HAVE_CONFIG_H
7 #  include <config.h>
8 #endif
9
10 #define NOPTS 129
11 #define MIN(a, b) (((a) < (b))? (a): (b))
12
13 static int convert_options(int sz_opts)
14 {
15     int co[NOPTS];
16     int i;
17     int opts = 0;
18
19     memset(co, 0, sizeof(int) * NOPTS);
20     co[SZ_MSB_OPTION_MASK] = AEC_DATA_MSB;
21     co[SZ_NN_OPTION_MASK] = AEC_DATA_PREPROCESS;
22
23     for (i = 1; i < NOPTS; i <<= 1)
24         if (sz_opts & i)
25             opts |= co[i];
26
27     return opts;
28 }
29
30 static int bits_to_bytes(int bit_length)
31 {
32     if (bit_length > 16)
33         return 4;
34     else if (bit_length > 8)
35         return 2;
36     else
37         return 1;
38 }
39
40 static void interleave_buffer(void *dest, const void *src,
41                               size_t n, int wordsize)
42 {
43     size_t i;
44     int j;
45     const unsigned char *src8;
46     unsigned char *dest8;
47
48     src8 = (unsigned char *)src;
49     dest8 = (unsigned char *)dest;
50
51     for (i = 0; i < n / wordsize; i++)
52         for (j = 0; j < wordsize; j++)
53             dest8[j * (n / wordsize) + i] = src8[i * wordsize + j];
54 }
55
56 static void deinterleave_buffer(void *dest, const void *src,
57                                 size_t n, int wordsize)
58 {
59     size_t i;
60     int j;
61     const unsigned char *src8;
62     unsigned char *dest8;
63
64     src8 = (unsigned char *)src;
65     dest8 = (unsigned char *)dest;
66
67     for (i = 0; i < n / wordsize; i++)
68         for (j = 0; j < wordsize; j++)
69             dest8[i * wordsize + j] = src8[j * (n / wordsize) + i];
70 }
71
72 static void add_padding(void *dest, const void *src, size_t total,
73                           size_t line_size, size_t padding_size,
74                           int pixel_size, int pp)
75 {
76     size_t i, j, k, ls, ps;
77     const char *pixel;
78     const char zero_pixel[] = {0, 0, 0, 0};
79
80     pixel = zero_pixel;
81     j = 0;
82     i = 0;
83     while (i < total) {
84         ls = MIN(total - i, line_size);
85         memcpy((char *)dest + j, (char *)src + i, ls);
86         j += ls;
87         i += ls;
88         if (pp)
89             pixel = (char *)src + i - pixel_size;
90         ps = line_size + padding_size - ls;
91         for (k = 0; k < ps; k += pixel_size)
92             memcpy((char *)dest + j + k, pixel, pixel_size);
93         j += ps;
94     }
95 }
96
97 static void remove_padding(void *buf, size_t total,
98                            size_t line_size, size_t padding_size,
99                            int pixel_size)
100 {
101     size_t i, j;
102     size_t padded_line_size = line_size + padding_size;
103
104     i = line_size;
105     for (j = padded_line_size; j < total; j += padded_line_size) {
106         memmove((char *)buf + i, (char *)buf + j, line_size);
107         i += line_size;
108     }
109 }
110
111 int SZ_BufftoBuffCompress(void *dest, size_t *destLen,
112                           const void *source, size_t sourceLen,
113                           SZ_com_t *param)
114 {
115     struct aec_stream strm;
116     int status;
117     int aec_status;
118     void *padbuf;
119     void *buf;
120     size_t padding_size;
121     size_t scanlines;
122     size_t padbuf_size;
123     int pixel_size;
124     int pad_scanline;
125     int interleave;
126
127     strm.block_size = param->pixels_per_block;
128     strm.rsi = (param->pixels_per_scanline + param->pixels_per_block - 1)
129         / param->pixels_per_block;
130     strm.flags = convert_options(param->options_mask);
131     strm.avail_out = *destLen;
132     strm.next_out = dest;
133     buf = 0;
134     padbuf = 0;
135
136     interleave = param->bits_per_pixel == 32 || param->bits_per_pixel == 64;
137     pad_scanline = param->pixels_per_scanline % param->pixels_per_block;
138     if (interleave) {
139         strm.bits_per_sample = 8;
140         buf = malloc(sourceLen);
141         if (buf == NULL) {
142             status = SZ_MEM_ERROR;
143             goto CLEANUP;
144         }
145         interleave_buffer(buf, source, sourceLen, param->bits_per_pixel / 8);
146     } else {
147         strm.bits_per_sample = param->bits_per_pixel;
148         buf = (void *)source;
149     }
150
151     pixel_size = bits_to_bytes(strm.bits_per_sample);
152
153     if (pad_scanline) {
154         scanlines = (sourceLen + param->pixels_per_scanline - 1)
155             / param->pixels_per_scanline;
156         padbuf_size = strm.rsi * strm.block_size * pixel_size * scanlines;
157         padbuf = malloc(padbuf_size);
158         if (padbuf == NULL) {
159             status = SZ_MEM_ERROR;
160             goto CLEANUP;
161         }
162
163         padding_size =
164             (strm.rsi * strm.block_size - param->pixels_per_scanline)
165             * pixel_size;
166
167         add_padding(padbuf, buf, sourceLen,
168                     param->pixels_per_scanline * pixel_size,
169                     padding_size, pixel_size,
170                     strm.flags & AEC_DATA_PREPROCESS);
171         strm.next_in = padbuf;
172         strm.avail_in = padbuf_size;
173     } else {
174         strm.next_in = buf;
175         strm.avail_in = sourceLen;
176     }
177
178     aec_status = aec_buffer_encode(&strm);
179     if (aec_status == AEC_STREAM_ERROR)
180         status = SZ_OUTBUFF_FULL;
181     else
182         status = aec_status;
183     *destLen = strm.total_out;
184
185 CLEANUP:
186     if (pad_scanline && padbuf)
187         free(padbuf);
188     if (interleave && buf)
189         free(buf);
190     return status;
191 }
192
193 int SZ_BufftoBuffDecompress(void *dest, size_t *destLen,
194                             const void *source, size_t sourceLen,
195                             SZ_com_t *param)
196 {
197     struct aec_stream strm;
198     int status;
199     void *buf;
200     size_t padding_size;
201     size_t scanlines;
202     size_t buf_size, total_out;
203     int pixel_size;
204     int pad_scanline;
205     int deinterleave;
206     int extra_buffer;
207
208     strm.block_size = param->pixels_per_block;
209     strm.rsi = (param->pixels_per_scanline + param->pixels_per_block - 1)
210         / param->pixels_per_block;
211     strm.flags = convert_options(param->options_mask);
212     strm.avail_in = sourceLen;
213     strm.next_in = source;
214     buf = 0;
215
216     pad_scanline = param->pixels_per_scanline % param->pixels_per_block;
217     deinterleave = param->bits_per_pixel == 32 || param->bits_per_pixel == 64;
218     extra_buffer = pad_scanline || deinterleave;
219
220     if (deinterleave)
221         strm.bits_per_sample = 8;
222     else
223         strm.bits_per_sample = param->bits_per_pixel;
224
225     pixel_size = bits_to_bytes(strm.bits_per_sample);
226
227     if (extra_buffer) {
228         if (pad_scanline) {
229             scanlines = (*destLen / pixel_size + param->pixels_per_scanline - 1)
230                 / param->pixels_per_scanline;
231             buf_size = strm.rsi * strm.block_size * pixel_size * scanlines;
232         } else {
233             buf_size = *destLen;
234         }
235         buf = malloc(buf_size);
236         if (buf == NULL) {
237             status = SZ_MEM_ERROR;
238             goto CLEANUP;
239         }
240         strm.next_out = buf;
241         strm.avail_out = buf_size;
242     } else {
243         strm.next_out = dest;
244         strm.avail_out = *destLen;
245     }
246
247     status = aec_buffer_decode(&strm);
248     if (status != AEC_OK)
249         goto CLEANUP;
250
251     if (pad_scanline) {
252         padding_size =
253             (strm.rsi * strm.block_size - param->pixels_per_scanline)
254             * pixel_size;
255         remove_padding(buf, strm.total_out,
256                        param->pixels_per_scanline * pixel_size,
257                        padding_size, pixel_size);
258         total_out = scanlines * param->pixels_per_scanline * pixel_size;
259     } else {
260         total_out = strm.total_out;
261     }
262
263     if (total_out < *destLen)
264         *destLen = total_out;
265
266     if (deinterleave)
267         deinterleave_buffer(dest, buf, *destLen, param->bits_per_pixel / 8);
268     else if (pad_scanline)
269         memcpy(dest, buf, *destLen);
270
271 CLEANUP:
272     if (extra_buffer && buf)
273         free(buf);
274
275     return status;
276 }
277
278 int SZ_encoder_enabled(void)
279 {
280     return 1;
281 }