1 /*
2 ** SPDX-License-Identifier: BSD-3-Clause
3 ** Copyright Contributors to the OpenEXR Project.
4 */
5 
6 #include "openexr_encode.h"
7 
8 #include "internal_coding.h"
9 #include "internal_compress.h"
10 #include "internal_structs.h"
11 #include "internal_xdr.h"
12 
13 /**************************************/
14 
15 static exr_result_t
default_compress_chunk(exr_encode_pipeline_t * encode)16 default_compress_chunk (exr_encode_pipeline_t* encode)
17 {
18     exr_result_t rv;
19     EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR_NO_LOCK (
20         encode->context, encode->part_index);
21 
22     rv = internal_encode_alloc_buffer (
23         encode,
24         EXR_TRANSCODE_BUFFER_COMPRESSED,
25         &(encode->compressed_buffer),
26         &(encode->compressed_alloc_size),
27         (((size_t) encode->packed_bytes) * (size_t) 110) / ((size_t) 100) +
28             65536);
29     if (rv != EXR_ERR_SUCCESS) return rv;
30 
31     switch (part->comp_type)
32     {
33         case EXR_COMPRESSION_NONE:
34             return pctxt->report_error (
35                 pctxt,
36                 EXR_ERR_INVALID_ARGUMENT,
37                 "no compresssion set but still trying to compress");
38 
39         case EXR_COMPRESSION_RLE: rv = internal_exr_apply_rle (encode); break;
40         case EXR_COMPRESSION_ZIP:
41         case EXR_COMPRESSION_ZIPS: rv = internal_exr_apply_zip (encode); break;
42         case EXR_COMPRESSION_PIZ: rv = internal_exr_apply_piz (encode); break;
43         case EXR_COMPRESSION_PXR24:
44             rv = internal_exr_apply_pxr24 (encode);
45             break;
46         case EXR_COMPRESSION_B44: rv = internal_exr_apply_b44 (encode); break;
47         case EXR_COMPRESSION_B44A: rv = internal_exr_apply_b44a (encode); break;
48         case EXR_COMPRESSION_DWAA: rv = internal_exr_apply_dwaa (encode); break;
49         case EXR_COMPRESSION_DWAB: rv = internal_exr_apply_dwab (encode); break;
50         case EXR_COMPRESSION_LAST_TYPE:
51         default:
52             return pctxt->print_error (
53                 pctxt,
54                 EXR_ERR_INVALID_ARGUMENT,
55                 "Compression technique 0x%02X invalid",
56                 (int) part->comp_type);
57     }
58     return rv;
59 }
60 
61 /**************************************/
62 
63 static exr_result_t
default_yield(exr_encode_pipeline_t * encode)64 default_yield (exr_encode_pipeline_t* encode)
65 {
66     exr_result_t rv;
67     EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR (
68         encode->context, encode->part_index);
69 
70     rv = internal_validate_next_chunk (encode, pctxt, part);
71 
72     return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (rv);
73 }
74 
75 /**************************************/
76 
77 static exr_result_t
default_write_chunk(exr_encode_pipeline_t * encode)78 default_write_chunk (exr_encode_pipeline_t* encode)
79 {
80     exr_result_t rv;
81 
82     if (!encode) return EXR_ERR_INVALID_ARGUMENT;
83 
84     switch (encode->chunk.type)
85     {
86         case EXR_STORAGE_SCANLINE:
87             rv = exr_write_scanline_chunk (
88                 EXR_CONST_CAST (exr_context_t, encode->context),
89                 encode->part_index,
90                 encode->chunk.start_y,
91                 encode->compressed_buffer,
92                 encode->compressed_bytes);
93             break;
94         case EXR_STORAGE_TILED:
95             rv = exr_write_tile_chunk (
96                 EXR_CONST_CAST (exr_context_t, encode->context),
97                 encode->part_index,
98                 encode->chunk.start_x,
99                 encode->chunk.start_y,
100                 encode->chunk.level_x,
101                 encode->chunk.level_y,
102                 encode->compressed_buffer,
103                 encode->compressed_bytes);
104             break;
105         case EXR_STORAGE_DEEP_SCANLINE:
106             if (!encode->packed_sample_count_table ||
107                 encode->packed_sample_count_bytes == 0)
108                 return EXR_ERR_INVALID_ARGUMENT;
109             rv = exr_write_deep_scanline_chunk (
110                 EXR_CONST_CAST (exr_context_t, encode->context),
111                 encode->part_index,
112                 encode->chunk.start_y,
113                 encode->compressed_buffer,
114                 encode->compressed_bytes,
115                 encode->packed_bytes,
116                 encode->packed_sample_count_table,
117                 encode->packed_sample_count_bytes);
118             break;
119         case EXR_STORAGE_DEEP_TILED:
120             if (!encode->packed_sample_count_table ||
121                 encode->packed_sample_count_bytes == 0)
122                 return EXR_ERR_INVALID_ARGUMENT;
123             rv = exr_write_deep_tile_chunk (
124                 EXR_CONST_CAST (exr_context_t, encode->context),
125                 encode->part_index,
126                 encode->chunk.start_x,
127                 encode->chunk.start_y,
128                 encode->chunk.level_x,
129                 encode->chunk.level_y,
130                 encode->compressed_buffer,
131                 encode->compressed_bytes,
132                 encode->packed_bytes,
133                 encode->packed_sample_count_table,
134                 encode->packed_sample_count_bytes);
135             break;
136         case EXR_STORAGE_LAST_TYPE:
137         default: rv = EXR_ERR_INVALID_ARGUMENT; break;
138     }
139     return rv;
140 }
141 
142 /**************************************/
143 
144 exr_result_t
exr_encoding_initialize(exr_const_context_t ctxt,int part_index,const exr_chunk_info_t * cinfo,exr_encode_pipeline_t * encode)145 exr_encoding_initialize (
146     exr_const_context_t           ctxt,
147     int                           part_index,
148     const exr_chunk_info_t* cinfo,
149     exr_encode_pipeline_t*        encode)
150 {
151     exr_result_t          rv;
152     exr_encode_pipeline_t nil = { 0 };
153 
154     EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
155     if (!cinfo || !encode)
156         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (
157             pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT));
158 
159     if (pctxt->mode != EXR_CONTEXT_WRITING_DATA)
160     {
161         if (pctxt->mode == EXR_CONTEXT_WRITE)
162             return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (
163                 pctxt->standard_error (pctxt, EXR_ERR_HEADER_NOT_WRITTEN));
164         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (
165             pctxt->standard_error (pctxt, EXR_ERR_NOT_OPEN_WRITE));
166     }
167 
168     *encode = nil;
169 
170     rv = internal_coding_fill_channel_info (
171         &(encode->channels),
172         &(encode->channel_count),
173         encode->_quick_chan_store,
174         cinfo,
175         pctxt,
176         part);
177 
178     if (rv == EXR_ERR_SUCCESS)
179     {
180         encode->part_index  = part_index;
181         encode->context     = ctxt;
182         encode->chunk = *cinfo;
183     }
184     return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (rv);
185 }
186 
187 /**************************************/
188 
189 exr_result_t
exr_encoding_choose_default_routines(exr_const_context_t ctxt,int part_index,exr_encode_pipeline_t * encode)190 exr_encoding_choose_default_routines (
191     exr_const_context_t ctxt, int part_index, exr_encode_pipeline_t* encode)
192 {
193     int32_t isdeep = 0;
194     EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
195     if (!encode)
196         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (
197             pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT));
198 
199     if (encode->context != ctxt || encode->part_index != part_index)
200         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->print_error (
201             pctxt,
202             EXR_ERR_INVALID_ARGUMENT,
203             "Cross-wired request for default routines from different context / part"));
204 
205     isdeep = (part->storage_mode == EXR_STORAGE_DEEP_SCANLINE ||
206               part->storage_mode == EXR_STORAGE_DEEP_TILED)
207                  ? 1
208                  : 0;
209 
210     encode->convert_and_pack_fn = internal_exr_match_encode (encode, isdeep);
211     if (part->comp_type != EXR_COMPRESSION_NONE)
212         encode->compress_fn = &default_compress_chunk;
213     encode->yield_until_ready_fn = &default_yield;
214     encode->write_fn             = &default_write_chunk;
215 
216     return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (EXR_ERR_SUCCESS);
217 }
218 
219 /**************************************/
220 
221 exr_result_t
exr_encoding_update(exr_const_context_t ctxt,int part_index,const exr_chunk_info_t * cinfo,exr_encode_pipeline_t * encode)222 exr_encoding_update (
223     exr_const_context_t           ctxt,
224     int                           part_index,
225     const exr_chunk_info_t* cinfo,
226     exr_encode_pipeline_t*        encode)
227 {
228     exr_result_t rv;
229 
230     EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
231     if (!cinfo || !encode)
232         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (
233             pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT));
234 
235     if (encode->context != ctxt || encode->part_index != part_index)
236         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->print_error (
237             pctxt,
238             EXR_ERR_INVALID_ARGUMENT,
239             "Cross-wired request for default routines from different context / part"));
240 
241     if (encode->packed_buffer == encode->compressed_buffer)
242         encode->compressed_buffer = NULL;
243 
244     encode->packed_bytes              = 0;
245     encode->packed_sample_count_bytes = 0;
246     encode->compressed_bytes          = 0;
247 
248     rv = internal_coding_update_channel_info (
249         encode->channels, encode->channel_count, cinfo, pctxt, part);
250 
251     if (rv == EXR_ERR_SUCCESS) encode->chunk = *cinfo;
252     return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (rv);
253 }
254 
255 /**************************************/
256 
257 exr_result_t
exr_encoding_run(exr_const_context_t ctxt,int part_index,exr_encode_pipeline_t * encode)258 exr_encoding_run (
259     exr_const_context_t ctxt, int part_index, exr_encode_pipeline_t* encode)
260 {
261     exr_result_t rv           = EXR_ERR_SUCCESS;
262     uint64_t     packed_bytes = 0;
263     EXR_PROMOTE_CONST_CONTEXT_AND_PART_OR_ERROR (ctxt, part_index);
264 
265     if (!encode)
266         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (
267             pctxt->standard_error (pctxt, EXR_ERR_INVALID_ARGUMENT));
268     if (encode->context != ctxt || encode->part_index != part_index)
269         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->report_error (
270             pctxt,
271             EXR_ERR_INVALID_ARGUMENT,
272             "Invalid request for encoding update from different context / part"));
273 
274     if (part->storage_mode == EXR_STORAGE_DEEP_SCANLINE ||
275         part->storage_mode == EXR_STORAGE_DEEP_TILED)
276     {
277         if (encode->sample_count_table == NULL ||
278             encode->sample_count_alloc_size !=
279                 (((size_t) encode->chunk.width) *
280                  ((size_t) encode->chunk.height) * sizeof (int32_t)))
281         {
282             return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->report_error (
283                 pctxt,
284                 EXR_ERR_INVALID_ARGUMENT,
285                 "Invalid / missing sample count table for deep data"));
286         }
287     }
288 
289     for (int c = 0; c < encode->channel_count; ++c)
290     {
291         const exr_coding_channel_info_t* encc = (encode->channels + c);
292 
293         if (encc->height == 0) continue;
294 
295         if (encc->width == 0)
296             return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->print_error (
297                 pctxt,
298                 EXR_ERR_INVALID_ARGUMENT,
299                 "Unexpected 0-width chunk to encode"));
300         if (!encc->encode_from_ptr)
301             return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->print_error (
302                 pctxt,
303                 EXR_ERR_INVALID_ARGUMENT,
304                 "Missing channel data pointer - must encode all channels"));
305 
306         /*
307          * if a user specifies a bad pixel stride / line stride
308          * we can't know this realistically, and they may want to
309          * use 0 to cause things to collapse for testing purposes
310          * so only test the values we can
311          */
312         if (encc->user_bytes_per_element != 2 &&
313             encc->user_bytes_per_element != 4)
314             return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->print_error (
315                 pctxt,
316                 EXR_ERR_INVALID_ARGUMENT,
317                 "Invalid / unsupported output bytes per element (%d) for channel %c (%s)",
318                 (int) encc->user_bytes_per_element,
319                 c,
320                 encc->channel_name));
321 
322         if (encc->user_data_type != (uint16_t) (EXR_PIXEL_HALF) &&
323             encc->user_data_type != (uint16_t) (EXR_PIXEL_FLOAT) &&
324             encc->user_data_type != (uint16_t) (EXR_PIXEL_UINT))
325             return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->print_error (
326                 pctxt,
327                 EXR_ERR_INVALID_ARGUMENT,
328                 "Invalid / unsupported output data type (%d) for channel %c (%s)",
329                 (int) encc->user_data_type,
330                 c,
331                 encc->channel_name));
332 
333         packed_bytes +=
334             ((uint64_t) (encc->height) * (uint64_t) (encc->width) *
335              (uint64_t) (encc->bytes_per_element));
336     }
337 
338     encode->packed_bytes = 0;
339     if (encode->convert_and_pack_fn)
340     {
341         if (packed_bytes > 0)
342         {
343             rv = internal_encode_alloc_buffer (
344                 encode,
345                 EXR_TRANSCODE_BUFFER_PACKED,
346                 &(encode->packed_buffer),
347                 &(encode->packed_alloc_size),
348                 packed_bytes);
349 
350             if (rv == EXR_ERR_SUCCESS)
351                 rv = encode->convert_and_pack_fn (encode);
352         }
353     }
354     else if (!encode->packed_buffer || packed_bytes != encode->compressed_bytes)
355     {
356         return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (pctxt->report_error (
357             pctxt,
358             EXR_ERR_INVALID_ARGUMENT,
359             "Encode pipeline has no packing function declared and packed buffer is null or appears to need packing"));
360     }
361     EXR_UNLOCK_WRITE (pctxt);
362 
363     if ((part->storage_mode == EXR_STORAGE_DEEP_SCANLINE ||
364          part->storage_mode == EXR_STORAGE_DEEP_TILED) &&
365         encode->sample_count_table != NULL)
366     {
367         priv_from_native32 (
368             encode->sample_count_table,
369             encode->chunk.width * encode->chunk.height);
370     }
371 
372     if (rv == EXR_ERR_SUCCESS)
373     {
374         if (encode->compress_fn && encode->packed_bytes > 0)
375         {
376             rv = encode->compress_fn (encode);
377         }
378         else
379         {
380             internal_encode_free_buffer (
381                 encode,
382                 EXR_TRANSCODE_BUFFER_COMPRESSED,
383                 &(encode->compressed_buffer),
384                 &(encode->compressed_alloc_size));
385 
386             internal_encode_free_buffer (
387                 encode,
388                 EXR_TRANSCODE_BUFFER_PACKED_SAMPLES,
389                 &(encode->packed_sample_count_table),
390                 &(encode->packed_sample_count_alloc_size));
391 
392             encode->compressed_buffer     = encode->packed_buffer;
393             encode->compressed_bytes      = encode->packed_bytes;
394             encode->compressed_alloc_size = 0;
395 
396             encode->packed_sample_count_table      = encode->sample_count_table;
397             encode->packed_sample_count_alloc_size = 0;
398             encode->packed_sample_count_bytes =
399                 (((size_t) encode->chunk.width) *
400                  ((size_t) encode->chunk.height) * sizeof (int32_t));
401         }
402     }
403 
404     if (rv == EXR_ERR_SUCCESS && encode->yield_until_ready_fn)
405         rv = encode->yield_until_ready_fn (encode);
406 
407     if (rv == EXR_ERR_SUCCESS && encode->write_fn)
408         rv = encode->write_fn (encode);
409 
410     if ((part->storage_mode == EXR_STORAGE_DEEP_SCANLINE ||
411          part->storage_mode == EXR_STORAGE_DEEP_TILED) &&
412         encode->sample_count_table != NULL)
413     {
414         priv_to_native32 (
415             encode->sample_count_table,
416             encode->chunk.width * encode->chunk.height);
417     }
418 
419     return rv;
420 }
421 
422 /**************************************/
423 
424 exr_result_t
exr_encoding_destroy(exr_const_context_t ctxt,exr_encode_pipeline_t * encode)425 exr_encoding_destroy (exr_const_context_t ctxt, exr_encode_pipeline_t* encode)
426 {
427     INTERN_EXR_PROMOTE_CONST_CONTEXT_OR_ERROR (ctxt);
428     if (encode)
429     {
430         exr_encode_pipeline_t nil = { 0 };
431         if (encode->channels != encode->_quick_chan_store)
432             pctxt->free_fn (encode->channels);
433 
434         internal_encode_free_buffer (
435             encode,
436             EXR_TRANSCODE_BUFFER_PACKED,
437             &(encode->packed_buffer),
438             &(encode->packed_alloc_size));
439         internal_encode_free_buffer (
440             encode,
441             EXR_TRANSCODE_BUFFER_COMPRESSED,
442             &(encode->compressed_buffer),
443             &(encode->compressed_alloc_size));
444         internal_encode_free_buffer (
445             encode,
446             EXR_TRANSCODE_BUFFER_SCRATCH1,
447             &(encode->scratch_buffer_1),
448             &(encode->scratch_alloc_size_1));
449         internal_encode_free_buffer (
450             encode,
451             EXR_TRANSCODE_BUFFER_SCRATCH2,
452             &(encode->scratch_buffer_2),
453             &(encode->scratch_alloc_size_2));
454         internal_encode_free_buffer (
455             encode,
456             EXR_TRANSCODE_BUFFER_PACKED_SAMPLES,
457             &(encode->packed_sample_count_table),
458             &(encode->packed_sample_count_alloc_size));
459         *encode = nil;
460     }
461     return EXR_UNLOCK_WRITE_AND_RETURN_PCTXT (EXR_ERR_SUCCESS);
462 }
463