| #ifdef USE_X3FTOOLS |
| |
| /* Library for accessing X3F Files |
| ---------------------------------------------------------------- |
| BSD-style License |
| ---------------------------------------------------------------- |
| |
| * Copyright (c) 2010, Roland Karlsson (roland@proxel.se) |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * * Neither the name of the organization nor the |
| * names of its contributors may be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY ROLAND KARLSSON ''AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL ROLAND KARLSSON BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| */ |
| |
| #include "third_party/libraw/internal/libraw_cxx_defs.h" |
| |
| #if defined __sun && defined DS |
| #undef DS |
| #endif |
| #ifdef ID |
| #undef ID /* used in x3f utils */ |
| #endif |
| |
| #include "third_party/libraw/internal/x3f_tools.h" |
| |
| /* extern */ int legacy_offset = 0; |
| /* extern */ bool_t auto_legacy_offset = 1; |
| |
| /* --------------------------------------------------------------------- */ |
| /* Reading and writing - assuming little endian in the file */ |
| /* --------------------------------------------------------------------- */ |
| |
| static void *x3f_limited_malloc(UINT64 sz) |
| { |
| if (sz > LIBRAW_X3F_ALLOC_LIMIT_MB * 1024ULL * 1024ULL) |
| throw LIBRAW_EXCEPTION_TOOBIG; |
| void *ret = malloc(sz); |
| if (!ret) |
| throw LIBRAW_EXCEPTION_ALLOC; |
| return ret; |
| } |
| |
| static void *x3f_limited_calloc(UINT64 n, UINT64 sz) |
| { |
| if (sz * n > LIBRAW_X3F_ALLOC_LIMIT_MB * 1024ULL * 1024ULL) |
| throw LIBRAW_EXCEPTION_TOOBIG; |
| void *ret = calloc(n, sz); |
| if (!ret) |
| throw LIBRAW_EXCEPTION_ALLOC; |
| return ret; |
| } |
| |
| static void *x3f_limited_realloc(void *ptr, UINT64 sz) |
| { |
| if (sz > LIBRAW_X3F_ALLOC_LIMIT_MB * 1024ULL * 1024ULL) |
| throw LIBRAW_EXCEPTION_TOOBIG; |
| void *ret = realloc(ptr, sz); |
| if (!ret) |
| throw LIBRAW_EXCEPTION_ALLOC; |
| return ret; |
| } |
| |
| |
| static int x3f_get1(LibRaw_abstract_datastream *f) |
| { |
| /* Little endian file */ |
| return f->get_char(); |
| } |
| |
| static int x3f_sget2(uchar *s) { return s[0] | s[1] << 8; } |
| |
| static int x3f_get2(LibRaw_abstract_datastream *f) |
| { |
| uchar str[2] = {0xff, 0xff}; |
| f->read(str, 1, 2); |
| return x3f_sget2(str); |
| } |
| |
| unsigned x3f_sget4(uchar *s) |
| { |
| return s[0] | s[1] << 8 | s[2] << 16 | s[3] << 24; |
| } |
| |
| unsigned x3f_get4(LibRaw_abstract_datastream *f) |
| { |
| uchar str[4] = {0xff, 0xff, 0xff, 0xff}; |
| f->read(str, 1, 4); |
| return x3f_sget4(str); |
| } |
| |
| #define FREE(P) \ |
| do \ |
| { \ |
| free(P); \ |
| (P) = NULL; \ |
| } while (0) |
| |
| #define PUT_GET_N(_buffer, _size, _file, _func) \ |
| do \ |
| { \ |
| int _left = _size; \ |
| while (_left != 0) \ |
| { \ |
| int _cur = _file->_func(_buffer, 1, _left); \ |
| if (_cur == 0) \ |
| { \ |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; \ |
| } \ |
| _left -= _cur; \ |
| } \ |
| } while (0) |
| |
| #define GET1(_v) \ |
| do \ |
| { \ |
| (_v) = x3f_get1(I->input.file); \ |
| } while (0) |
| #define GET2(_v) \ |
| do \ |
| { \ |
| (_v) = x3f_get2(I->input.file); \ |
| } while (0) |
| #define GET4(_v) \ |
| do \ |
| { \ |
| (_v) = x3f_get4(I->input.file); \ |
| } while (0) |
| |
| #define GET4F(_v) \ |
| do \ |
| { \ |
| union { \ |
| int32_t i; \ |
| float f; \ |
| } _tmp; \ |
| _tmp.i = x3f_get4(I->input.file); \ |
| (_v) = _tmp.f; \ |
| } while (0) |
| |
| #define GETN(_v, _s) PUT_GET_N(_v, _s, I->input.file, read) |
| |
| #define GET_TABLE(_T, _GETX, _NUM, _TYPE) \ |
| do \ |
| { \ |
| int _i; \ |
| (_T).size = (_NUM); \ |
| (_T).element = \ |
| (_TYPE *)x3f_limited_realloc((_T).element, (_NUM) * sizeof((_T).element[0])); \ |
| for (_i = 0; _i < (int)(_T).size; _i++) \ |
| _GETX((_T).element[_i]); \ |
| } while (0) |
| |
| #define GET_PROPERTY_TABLE(_T, _NUM) \ |
| do \ |
| { \ |
| int _i; \ |
| (_T).size = (_NUM); \ |
| (_T).element = (x3f_property_t *)x3f_limited_realloc( \ |
| (_T).element, (_NUM) * sizeof((_T).element[0])); \ |
| for (_i = 0; _i < (int)(_T).size; _i++) \ |
| { \ |
| GET4((_T).element[_i].name_offset); \ |
| GET4((_T).element[_i].value_offset); \ |
| } \ |
| } while (0) |
| |
| #define GET_TRUE_HUFF_TABLE(_T) \ |
| do \ |
| { \ |
| int _i; \ |
| (_T).element = NULL; \ |
| for (_i = 0;; _i++) \ |
| { \ |
| (_T).size = _i + 1; \ |
| (_T).element = (x3f_true_huffman_element_t *)x3f_limited_realloc( \ |
| (_T).element, (_i + 1) * sizeof((_T).element[0])); \ |
| GET1((_T).element[_i].code_size); \ |
| GET1((_T).element[_i].code); \ |
| if ((_T).element[_i].code_size == 0) \ |
| break; \ |
| } \ |
| } while (0) |
| |
| /* --------------------------------------------------------------------- */ |
| /* Allocating Huffman tree help data */ |
| /* --------------------------------------------------------------------- */ |
| |
| static void cleanup_huffman_tree(x3f_hufftree_t *HTP) { free(HTP->nodes); } |
| |
| static void new_huffman_tree(x3f_hufftree_t *HTP, int bits) |
| { |
| int leaves = 1 << bits; |
| |
| HTP->free_node_index = 0; |
| HTP->total_node_index = HUF_TREE_MAX_NODES(leaves); |
| HTP->nodes = (x3f_huffnode_t *)x3f_limited_calloc(1, HUF_TREE_MAX_NODES(leaves) * |
| sizeof(x3f_huffnode_t)); |
| } |
| |
| /* --------------------------------------------------------------------- */ |
| /* Allocating TRUE engine RAW help data */ |
| /* --------------------------------------------------------------------- */ |
| |
| static void cleanup_true(x3f_true_t **TRUP) |
| { |
| x3f_true_t *TRU = *TRUP; |
| |
| if (TRU == NULL) |
| return; |
| |
| FREE(TRU->table.element); |
| FREE(TRU->plane_size.element); |
| cleanup_huffman_tree(&TRU->tree); |
| FREE(TRU->x3rgb16.buf); |
| |
| FREE(TRU); |
| |
| *TRUP = NULL; |
| } |
| |
| static x3f_true_t *new_true(x3f_true_t **TRUP) |
| { |
| x3f_true_t *TRU = (x3f_true_t *)x3f_limited_calloc(1, sizeof(x3f_true_t)); |
| |
| cleanup_true(TRUP); |
| |
| TRU->table.size = 0; |
| TRU->table.element = NULL; |
| TRU->plane_size.size = 0; |
| TRU->plane_size.element = NULL; |
| TRU->tree.nodes = NULL; |
| TRU->x3rgb16.data = NULL; |
| TRU->x3rgb16.buf = NULL; |
| |
| *TRUP = TRU; |
| |
| return TRU; |
| } |
| |
| static void cleanup_quattro(x3f_quattro_t **QP) |
| { |
| x3f_quattro_t *Q = *QP; |
| |
| if (Q == NULL) |
| return; |
| |
| FREE(Q->top16.buf); |
| FREE(Q); |
| |
| *QP = NULL; |
| } |
| |
| static x3f_quattro_t *new_quattro(x3f_quattro_t **QP) |
| { |
| x3f_quattro_t *Q = (x3f_quattro_t *)x3f_limited_calloc(1, sizeof(x3f_quattro_t)); |
| int i; |
| |
| cleanup_quattro(QP); |
| |
| for (i = 0; i < TRUE_PLANES; i++) |
| { |
| Q->plane[i].columns = 0; |
| Q->plane[i].rows = 0; |
| } |
| |
| Q->unknown = 0; |
| |
| Q->top16.data = NULL; |
| Q->top16.buf = NULL; |
| |
| *QP = Q; |
| |
| return Q; |
| } |
| |
| /* --------------------------------------------------------------------- */ |
| /* Allocating Huffman engine help data */ |
| /* --------------------------------------------------------------------- */ |
| |
| static void cleanup_huffman(x3f_huffman_t **HUFP) |
| { |
| x3f_huffman_t *HUF = *HUFP; |
| |
| if (HUF == NULL) |
| return; |
| |
| FREE(HUF->mapping.element); |
| FREE(HUF->table.element); |
| cleanup_huffman_tree(&HUF->tree); |
| FREE(HUF->row_offsets.element); |
| FREE(HUF->rgb8.buf); |
| FREE(HUF->x3rgb16.buf); |
| FREE(HUF); |
| |
| *HUFP = NULL; |
| } |
| |
| static x3f_huffman_t *new_huffman(x3f_huffman_t **HUFP) |
| { |
| x3f_huffman_t *HUF = (x3f_huffman_t *)x3f_limited_calloc(1, sizeof(x3f_huffman_t)); |
| |
| cleanup_huffman(HUFP); |
| |
| /* Set all not read data block pointers to NULL */ |
| HUF->mapping.size = 0; |
| HUF->mapping.element = NULL; |
| HUF->table.size = 0; |
| HUF->table.element = NULL; |
| HUF->tree.nodes = NULL; |
| HUF->row_offsets.size = 0; |
| HUF->row_offsets.element = NULL; |
| HUF->rgb8.data = NULL; |
| HUF->rgb8.buf = NULL; |
| HUF->x3rgb16.data = NULL; |
| HUF->x3rgb16.buf = NULL; |
| |
| *HUFP = HUF; |
| |
| return HUF; |
| } |
| |
| /* --------------------------------------------------------------------- */ |
| /* Creating a new x3f structure from file */ |
| /* --------------------------------------------------------------------- */ |
| |
| /* extern */ x3f_t *x3f_new_from_file(LibRaw_abstract_datastream *infile) |
| { |
| if (!infile) |
| return NULL; |
| INT64 fsize = infile->size(); |
| x3f_t *x3f = (x3f_t *)x3f_limited_calloc(1, sizeof(x3f_t)); |
| try |
| { |
| x3f_info_t *I = NULL; |
| x3f_header_t *H = NULL; |
| x3f_directory_section_t *DS = NULL; |
| int i, d; |
| |
| I = &x3f->info; |
| I->error = NULL; |
| I->input.file = infile; |
| I->output.file = NULL; |
| |
| /* Read file header */ |
| H = &x3f->header; |
| infile->seek(0, SEEK_SET); |
| GET4(H->identifier); |
| |
| if (H->identifier != X3F_FOVb) |
| { |
| free(x3f); |
| return NULL; |
| } |
| |
| GET4(H->version); |
| if (H->version < X3F_VERSION_4_0) |
| { |
| GETN(H->unique_identifier, SIZE_UNIQUE_IDENTIFIER); |
| GET4(H->mark_bits); |
| GET4(H->columns); |
| GET4(H->rows); |
| GET4(H->rotation); |
| if (H->version >= X3F_VERSION_2_1) |
| { |
| int num_ext_data = |
| H->version >= X3F_VERSION_3_0 ? NUM_EXT_DATA_3_0 : NUM_EXT_DATA_2_1; |
| |
| GETN(H->white_balance, SIZE_WHITE_BALANCE); |
| if (H->version >= X3F_VERSION_2_3) |
| GETN(H->color_mode, SIZE_COLOR_MODE); |
| GETN(H->extended_types, num_ext_data); |
| for (i = 0; i < num_ext_data; i++) |
| GET4F(H->extended_data[i]); |
| } |
| } |
| else if (H->version < X3F_VERSION(5, 0)) |
| { |
| /* TODO: the meaning of the rest of the header for version >= 4.0 |
| * (Quattro) is unknown */ |
| infile->seek(12, SEEK_CUR); |
| GETN(H->unique_identifier, SIZE_UNIQUE_IDENTIFIER); |
| infile->seek(4, SEEK_CUR); |
| GET4(H->columns); |
| GET4(H->rows); |
| GET4(H->rotation); |
| } |
| |
| /* Go to the beginning of the directory */ |
| infile->seek(-4, SEEK_END); |
| infile->seek(x3f_get4(infile), SEEK_SET); |
| |
| /* Read the directory header */ |
| DS = &x3f->directory_section; |
| GET4(DS->identifier); |
| GET4(DS->version); |
| GET4(DS->num_directory_entries); |
| |
| if (DS->num_directory_entries > 50) |
| goto _err; // too much direntries, most likely broken file |
| |
| if (DS->num_directory_entries > 0) |
| { |
| size_t size = DS->num_directory_entries * sizeof(x3f_directory_entry_t); |
| DS->directory_entry = (x3f_directory_entry_t *)x3f_limited_calloc(1, size); |
| } |
| |
| /* Traverse the directory */ |
| for (d = 0; d < (int)DS->num_directory_entries; d++) |
| { |
| x3f_directory_entry_t *DE = &DS->directory_entry[d]; |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| INT64 save_dir_pos; |
| |
| /* Read the directory entry info */ |
| GET4(DE->input.offset); |
| GET4(DE->input.size); |
| if (DE->input.offset + DE->input.size > fsize * 2) |
| goto _err; |
| |
| DE->output.offset = 0; |
| DE->output.size = 0; |
| |
| GET4(DE->type); |
| |
| /* Save current pos and go to the entry */ |
| save_dir_pos = infile->tell(); |
| infile->seek(DE->input.offset, SEEK_SET); |
| |
| /* Read the type independent part of the entry header */ |
| DEH = &DE->header; |
| GET4(DEH->identifier); |
| GET4(DEH->version); |
| |
| /* NOTE - the tests below could be made on DE->type instead */ |
| |
| if (DEH->identifier == X3F_SECp) |
| { |
| x3f_property_list_t *PL = &DEH->data_subsection.property_list; |
| if (!PL) |
| goto _err; |
| /* Read the property part of the header */ |
| GET4(PL->num_properties); |
| GET4(PL->character_format); |
| GET4(PL->reserved); |
| GET4(PL->total_length); |
| |
| /* Set all not read data block pointers to NULL */ |
| PL->data = NULL; |
| PL->data_size = 0; |
| } |
| |
| if (DEH->identifier == X3F_SECi) |
| { |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| if (!ID) |
| goto _err; |
| /* Read the image part of the header */ |
| GET4(ID->type); |
| GET4(ID->format); |
| ID->type_format = (ID->type << 16) + (ID->format); |
| GET4(ID->columns); |
| GET4(ID->rows); |
| GET4(ID->row_stride); |
| |
| /* Set all not read data block pointers to NULL */ |
| ID->huffman = NULL; |
| |
| ID->data = NULL; |
| ID->data_size = 0; |
| } |
| |
| if (DEH->identifier == X3F_SECc) |
| { |
| x3f_camf_t *CAMF = &DEH->data_subsection.camf; |
| if (!CAMF) |
| goto _err; |
| /* Read the CAMF part of the header */ |
| GET4(CAMF->type); |
| GET4(CAMF->tN.val0); |
| GET4(CAMF->tN.val1); |
| GET4(CAMF->tN.val2); |
| GET4(CAMF->tN.val3); |
| |
| /* Set all not read data block pointers to NULL */ |
| CAMF->data = NULL; |
| CAMF->data_size = 0; |
| |
| /* Set all not allocated help pointers to NULL */ |
| CAMF->table.element = NULL; |
| CAMF->table.size = 0; |
| CAMF->tree.nodes = NULL; |
| CAMF->decoded_data = NULL; |
| CAMF->decoded_data_size = 0; |
| CAMF->entry_table.element = NULL; |
| CAMF->entry_table.size = 0; |
| } |
| |
| /* Reset the file pointer back to the directory */ |
| infile->seek(save_dir_pos, SEEK_SET); |
| } |
| |
| return x3f; |
| _err: |
| if (x3f) |
| { |
| DS = &x3f->directory_section; |
| if (DS && DS->directory_entry) |
| free(DS->directory_entry); |
| free(x3f); |
| } |
| return NULL; |
| } |
| catch (...) |
| { |
| x3f_directory_section_t *DS = &x3f->directory_section; |
| if (DS && DS->directory_entry) |
| free(DS->directory_entry); |
| free(x3f); |
| return NULL; |
| } |
| } |
| |
| /* --------------------------------------------------------------------- */ |
| /* Clean up an x3f structure */ |
| /* --------------------------------------------------------------------- */ |
| |
| static void free_camf_entry(camf_entry_t *entry) |
| { |
| FREE(entry->property_name); |
| FREE(entry->property_value); |
| FREE(entry->matrix_decoded); |
| FREE(entry->matrix_dim_entry); |
| } |
| |
| /* extern */ x3f_return_t x3f_delete(x3f_t *x3f) |
| { |
| x3f_directory_section_t *DS; |
| int d; |
| |
| if (x3f == NULL) |
| return X3F_ARGUMENT_ERROR; |
| |
| DS = &x3f->directory_section; |
| if (DS->num_directory_entries > 50) |
| return X3F_ARGUMENT_ERROR; |
| |
| for (d = 0; d < (int)DS->num_directory_entries; d++) |
| { |
| x3f_directory_entry_t *DE = &DS->directory_entry[d]; |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| if (DEH->identifier == X3F_SECp) |
| { |
| x3f_property_list_t *PL = &DEH->data_subsection.property_list; |
| FREE(PL->property_table.element); |
| FREE(PL->data); |
| } |
| |
| if (DEH->identifier == X3F_SECi) |
| { |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| |
| if (ID) |
| { |
| cleanup_huffman(&ID->huffman); |
| cleanup_true(&ID->tru); |
| cleanup_quattro(&ID->quattro); |
| FREE(ID->data); |
| } |
| } |
| |
| if (DEH->identifier == X3F_SECc) |
| { |
| x3f_camf_t *CAMF = &DEH->data_subsection.camf; |
| int i; |
| if (CAMF) |
| { |
| FREE(CAMF->data); |
| FREE(CAMF->table.element); |
| cleanup_huffman_tree(&CAMF->tree); |
| FREE(CAMF->decoded_data); |
| for (i = 0; i < (int)CAMF->entry_table.size; i++) |
| { |
| free_camf_entry(&CAMF->entry_table.element[i]); |
| } |
| } |
| FREE(CAMF->entry_table.element); |
| } |
| } |
| |
| FREE(DS->directory_entry); |
| FREE(x3f); |
| |
| return X3F_OK; |
| } |
| |
| /* --------------------------------------------------------------------- */ |
| /* Getting a reference to a directory entry */ |
| /* --------------------------------------------------------------------- */ |
| |
| /* TODO: all those only get the first instance */ |
| |
| static x3f_directory_entry_t *x3f_get(x3f_t *x3f, uint32_t type, |
| uint32_t image_type) |
| { |
| x3f_directory_section_t *DS; |
| int d; |
| |
| if (x3f == NULL) |
| return NULL; |
| |
| DS = &x3f->directory_section; |
| |
| for (d = 0; d < (int)DS->num_directory_entries; d++) |
| { |
| x3f_directory_entry_t *DE = &DS->directory_entry[d]; |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| |
| if (DEH->identifier == type) |
| { |
| switch (DEH->identifier) |
| { |
| case X3F_SECi: |
| { |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| |
| if (ID->type_format == image_type) |
| return DE; |
| } |
| break; |
| default: |
| return DE; |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| /* extern */ x3f_directory_entry_t *x3f_get_raw(x3f_t *x3f) |
| { |
| x3f_directory_entry_t *DE; |
| |
| if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_HUFFMAN_X530)) != NULL) |
| return DE; |
| |
| if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_HUFFMAN_10BIT)) != NULL) |
| return DE; |
| |
| if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_TRUE)) != NULL) |
| return DE; |
| |
| if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_MERRILL)) != NULL) |
| return DE; |
| |
| if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_QUATTRO)) != NULL) |
| return DE; |
| |
| if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_SDQ)) != NULL) |
| return DE; |
| |
| if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_SDQH)) != NULL) |
| return DE; |
| if ((DE = x3f_get(x3f, X3F_SECi, X3F_IMAGE_RAW_SDQH2)) != NULL) |
| return DE; |
| |
| return NULL; |
| } |
| |
| /* extern */ x3f_directory_entry_t *x3f_get_thumb_plain(x3f_t *x3f) |
| { |
| return x3f_get(x3f, X3F_SECi, X3F_IMAGE_THUMB_PLAIN); |
| } |
| |
| /* extern */ x3f_directory_entry_t *x3f_get_thumb_huffman(x3f_t *x3f) |
| { |
| return x3f_get(x3f, X3F_SECi, X3F_IMAGE_THUMB_HUFFMAN); |
| } |
| |
| /* extern */ x3f_directory_entry_t *x3f_get_thumb_jpeg(x3f_t *x3f) |
| { |
| return x3f_get(x3f, X3F_SECi, X3F_IMAGE_THUMB_JPEG); |
| } |
| |
| /* extern */ x3f_directory_entry_t *x3f_get_camf(x3f_t *x3f) |
| { |
| return x3f_get(x3f, X3F_SECc, 0); |
| } |
| |
| /* extern */ x3f_directory_entry_t *x3f_get_prop(x3f_t *x3f) |
| { |
| return x3f_get(x3f, X3F_SECp, 0); |
| } |
| |
| /* For some obscure reason, the bit numbering is weird. It is |
| generally some kind of "big endian" style - e.g. the bit 7 is the |
| first in a byte and bit 31 first in a 4 byte int. For patterns in |
| the huffman pattern table, bit 27 is the first bit and bit 26 the |
| next one. */ |
| |
| #define PATTERN_BIT_POS(_len, _bit) ((_len) - (_bit)-1) |
| #define MEMORY_BIT_POS(_bit) PATTERN_BIT_POS(8, _bit) |
| |
| /* --------------------------------------------------------------------- */ |
| /* Huffman Decode */ |
| /* --------------------------------------------------------------------- */ |
| |
| /* Make the huffman tree */ |
| |
| #ifdef DBG_PRNT |
| static char *display_code(int length, uint32_t code, char *buffer) |
| { |
| int i; |
| |
| for (i = 0; i < length; i++) |
| { |
| int pos = PATTERN_BIT_POS(length, i); |
| buffer[i] = ((code >> pos) & 1) == 0 ? '0' : '1'; |
| } |
| |
| buffer[i] = 0; |
| |
| return buffer; |
| } |
| #endif |
| |
| static x3f_huffnode_t *new_node(x3f_hufftree_t *tree) |
| { |
| if (tree->free_node_index >= tree->total_node_index) |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| x3f_huffnode_t *t = &tree->nodes[tree->free_node_index]; |
| |
| t->branch[0] = NULL; |
| t->branch[1] = NULL; |
| t->leaf = UNDEFINED_LEAF; |
| |
| tree->free_node_index++; |
| |
| return t; |
| } |
| |
| static void add_code_to_tree(x3f_hufftree_t *tree, int length, uint32_t code, |
| uint32_t value) |
| { |
| int i; |
| |
| x3f_huffnode_t *t = tree->nodes; |
| |
| for (i = 0; i < length; i++) |
| { |
| int pos = PATTERN_BIT_POS(length, i); |
| int bit = (code >> pos) & 1; |
| x3f_huffnode_t *t_next = t->branch[bit]; |
| |
| if (t_next == NULL) |
| t_next = t->branch[bit] = new_node(tree); |
| |
| t = t_next; |
| } |
| |
| t->leaf = value; |
| } |
| |
| static void populate_true_huffman_tree(x3f_hufftree_t *tree, |
| x3f_true_huffman_t *table) |
| { |
| int i; |
| |
| new_node(tree); |
| |
| for (i = 0; i < (int)table->size; i++) |
| { |
| x3f_true_huffman_element_t *element = &table->element[i]; |
| uint32_t length = element->code_size; |
| |
| if (length != 0) |
| { |
| /* add_code_to_tree wants the code right adjusted */ |
| uint32_t code = ((element->code) >> (8 - length)) & 0xff; |
| uint32_t value = i; |
| |
| add_code_to_tree(tree, length, code, value); |
| |
| #ifdef DBG_PRNT |
| { |
| char buffer[100]; |
| |
| x3f_printf(DEBUG, "H %5d : %5x : %5d : %02x %08x (%08x) (%s)\n", i, i, |
| value, length, code, value, |
| display_code(length, code, buffer)); |
| } |
| #endif |
| } |
| } |
| } |
| |
| static void populate_huffman_tree(x3f_hufftree_t *tree, x3f_table32_t *table, |
| x3f_table16_t *mapping) |
| { |
| int i; |
| |
| new_node(tree); |
| |
| for (i = 0; i < (int)table->size; i++) |
| { |
| uint32_t element = table->element[i]; |
| |
| if (element != 0) |
| { |
| uint32_t length = HUF_TREE_GET_LENGTH(element); |
| uint32_t code = HUF_TREE_GET_CODE(element); |
| uint32_t value; |
| |
| /* If we have a valid mapping table - then the value from the |
| mapping table shall be used. Otherwise we use the current |
| index in the table as value. */ |
| if (table->size == mapping->size) |
| value = mapping->element[i]; |
| else |
| value = i; |
| |
| add_code_to_tree(tree, length, code, value); |
| |
| #ifdef DBG_PRNT |
| { |
| char buffer[100]; |
| |
| x3f_printf(DEBUG, "H %5d : %5x : %5d : %02x %08x (%08x) (%s)\n", i, i, |
| value, length, code, element, |
| display_code(length, code, buffer)); |
| } |
| #endif |
| } |
| } |
| } |
| |
| #ifdef DBG_PRNT |
| static void print_huffman_tree(x3f_huffnode_t *t, int length, uint32_t code) |
| { |
| char buf1[100]; |
| char buf2[100]; |
| |
| x3f_printf(DEBUG, "%*s (%s,%s) %s (%s)\n", length, |
| length < 1 ? "-" : (code & 1) ? "1" : "0", |
| t->branch[0] == NULL ? "-" : "0", t->branch[1] == NULL ? "-" : "1", |
| t->leaf == UNDEFINED_LEAF ? "-" |
| : (sprintf(buf1, "%x", t->leaf), buf1), |
| display_code(length, code, buf2)); |
| |
| code = code << 1; |
| if (t->branch[0]) |
| print_huffman_tree(t->branch[0], length + 1, code + 0); |
| if (t->branch[1]) |
| print_huffman_tree(t->branch[1], length + 1, code + 1); |
| } |
| #endif |
| |
| /* Help machinery for reading bits in a memory */ |
| |
| typedef struct bit_state_s |
| { |
| uint8_t *next_address; |
| uint8_t bit_offset; |
| uint8_t bits[8]; |
| } bit_state_t; |
| |
| static void set_bit_state(bit_state_t *BS, uint8_t *address) |
| { |
| BS->next_address = address; |
| BS->bit_offset = 8; |
| } |
| |
| static uint8_t get_bit(bit_state_t *BS) |
| { |
| if (BS->bit_offset == 8) |
| { |
| uint8_t byte = *BS->next_address; |
| int i; |
| |
| for (i = 7; i >= 0; i--) |
| { |
| BS->bits[i] = byte & 1; |
| byte = byte >> 1; |
| } |
| BS->next_address++; |
| BS->bit_offset = 0; |
| } |
| |
| return BS->bits[BS->bit_offset++]; |
| } |
| |
| /* Decode use the TRUE algorithm */ |
| |
| static int32_t get_true_diff(bit_state_t *BS, x3f_hufftree_t *HTP) |
| { |
| int32_t diff; |
| x3f_huffnode_t *node = &HTP->nodes[0]; |
| uint8_t bits; |
| |
| while (node->branch[0] != NULL || node->branch[1] != NULL) |
| { |
| uint8_t bit = get_bit(BS); |
| x3f_huffnode_t *new_node = node->branch[bit]; |
| |
| node = new_node; |
| if (node == NULL) |
| { |
| /* TODO: Shouldn't this be treated as a fatal error? */ |
| return 0; |
| } |
| } |
| |
| bits = node->leaf; |
| |
| if (bits == 0) |
| diff = 0; |
| else |
| { |
| uint8_t first_bit = get_bit(BS); |
| int i; |
| |
| diff = first_bit; |
| |
| for (i = 1; i < bits; i++) |
| diff = (diff << 1) + get_bit(BS); |
| |
| if (first_bit == 0) |
| diff -= (1 << bits) - 1; |
| } |
| |
| return diff; |
| } |
| |
| /* This code (that decodes one of the X3F color planes, really is a |
| decoding of a compression algorithm suited for Bayer CFA data. In |
| Bayer CFA the data is divided into 2x2 squares that represents |
| (R,G1,G2,B) data. Those four positions are (in this compression) |
| treated as one data stream each, where you store the differences to |
| previous data in the stream. The reason for this is, of course, |
| that the date is more often than not near to the next data in a |
| stream that represents the same color. */ |
| |
| /* TODO: write more about the compression */ |
| |
| static void true_decode_one_color(x3f_image_data_t *ID, int color) |
| { |
| x3f_true_t *TRU = ID->tru; |
| x3f_quattro_t *Q = ID->quattro; |
| uint32_t seed = TRU->seed[color]; /* TODO : Is this correct ? */ |
| int row; |
| |
| x3f_hufftree_t *tree = &TRU->tree; |
| bit_state_t BS; |
| |
| int32_t row_start_acc[2][2]; |
| uint32_t rows = ID->rows; |
| uint32_t cols = ID->columns; |
| x3f_area16_t *area = &TRU->x3rgb16; |
| uint16_t *dst = area->data + color; |
| |
| set_bit_state(&BS, TRU->plane_address[color]); |
| |
| row_start_acc[0][0] = seed; |
| row_start_acc[0][1] = seed; |
| row_start_acc[1][0] = seed; |
| row_start_acc[1][1] = seed; |
| |
| if (ID->type_format == X3F_IMAGE_RAW_QUATTRO || |
| ID->type_format == X3F_IMAGE_RAW_SDQ || |
| ID->type_format == X3F_IMAGE_RAW_SDQH || |
| ID->type_format == X3F_IMAGE_RAW_SDQH2) |
| { |
| rows = Q->plane[color].rows; |
| cols = Q->plane[color].columns; |
| |
| if (Q->quattro_layout && color == 2) |
| { |
| area = &Q->top16; |
| dst = area->data; |
| } |
| } |
| else |
| { |
| } |
| |
| if (rows != area->rows || cols < area->columns) |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| |
| for (row = 0; row < (int)rows; row++) |
| { |
| int col; |
| bool_t odd_row = row & 1; |
| int32_t acc[2]; |
| |
| for (col = 0; col < (int)cols; col++) |
| { |
| bool_t odd_col = col & 1; |
| int32_t diff = get_true_diff(&BS, tree); |
| int32_t prev = col < 2 ? row_start_acc[odd_row][odd_col] : acc[odd_col]; |
| int32_t value = prev + diff; |
| |
| acc[odd_col] = value; |
| if (col < 2) |
| row_start_acc[odd_row][odd_col] = value; |
| |
| /* Discard additional data at the right for binned Quattro plane 2 */ |
| if (col >= (int)area->columns) |
| continue; |
| |
| *dst = value; |
| dst += area->channels; |
| } |
| } |
| } |
| |
| static void true_decode(x3f_info_t * /*I*/, x3f_directory_entry_t *DE) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| int color; |
| |
| for (color = 0; color < 3; color++) |
| { |
| true_decode_one_color(ID, color); |
| } |
| } |
| |
| /* Decode use the huffman tree */ |
| |
| static int32_t get_huffman_diff(bit_state_t *BS, x3f_hufftree_t *HTP) |
| { |
| int32_t diff; |
| x3f_huffnode_t *node = &HTP->nodes[0]; |
| |
| while (node->branch[0] != NULL || node->branch[1] != NULL) |
| { |
| uint8_t bit = get_bit(BS); |
| x3f_huffnode_t *new_node = node->branch[bit]; |
| |
| node = new_node; |
| if (node == NULL) |
| { |
| /* TODO: Shouldn't this be treated as a fatal error? */ |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| return 0; /* unreachable code */ |
| } |
| } |
| |
| diff = node->leaf; |
| |
| return diff; |
| } |
| |
| static void huffman_decode_row(x3f_info_t * /*I*/, x3f_directory_entry_t *DE, |
| int /*bits*/, int row, int offset, int *minimum) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| x3f_huffman_t *HUF = ID->huffman; |
| |
| int16_t c[3] = {(int16_t)offset, (int16_t)offset, (int16_t)offset}; |
| int col; |
| bit_state_t BS; |
| |
| if (HUF->row_offsets.element[row] > ID->data_size - 1) |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| set_bit_state(&BS, (uint8_t *)ID->data + HUF->row_offsets.element[row]); |
| |
| for (col = 0; col < (int)ID->columns; col++) |
| { |
| int color; |
| |
| for (color = 0; color < 3; color++) |
| { |
| uint16_t c_fix; |
| |
| c[color] += get_huffman_diff(&BS, &HUF->tree); |
| if (c[color] < 0) |
| { |
| c_fix = 0; |
| if (c[color] < *minimum) |
| *minimum = c[color]; |
| } |
| else |
| { |
| c_fix = c[color]; |
| } |
| |
| switch (ID->type_format) |
| { |
| case X3F_IMAGE_RAW_HUFFMAN_X530: |
| case X3F_IMAGE_RAW_HUFFMAN_10BIT: |
| HUF->x3rgb16.data[3 * (row * ID->columns + col) + color] = |
| (uint16_t)c_fix; |
| break; |
| case X3F_IMAGE_THUMB_HUFFMAN: |
| HUF->rgb8.data[3 * (row * ID->columns + col) + color] = (uint8_t)c_fix; |
| break; |
| default: |
| /* TODO: Shouldn't this be treated as a fatal error? */ |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| } |
| } |
| } |
| |
| static void huffman_decode(x3f_info_t *I, x3f_directory_entry_t *DE, int bits) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| |
| int row; |
| int minimum = 0; |
| int offset = legacy_offset; |
| |
| for (row = 0; row < (int)ID->rows; row++) |
| huffman_decode_row(I, DE, bits, row, offset, &minimum); |
| |
| if (auto_legacy_offset && minimum < 0) |
| { |
| offset = -minimum; |
| for (row = 0; row < (int)ID->rows; row++) |
| huffman_decode_row(I, DE, bits, row, offset, &minimum); |
| } |
| } |
| |
| static int32_t get_simple_diff(x3f_huffman_t *HUF, uint16_t index) |
| { |
| if (HUF->mapping.size == 0) |
| return index; |
| else |
| return HUF->mapping.element[index]; |
| } |
| |
| static void simple_decode_row(x3f_info_t * /*I*/, x3f_directory_entry_t *DE, |
| int bits, int row, int row_stride) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| x3f_huffman_t *HUF = ID->huffman; |
| |
| if (row*row_stride > (int)(ID->data_size - (ID->columns*sizeof(uint32_t)))) |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| uint32_t *data = (uint32_t *)((unsigned char *)ID->data + row * row_stride); |
| |
| uint16_t c[3] = {0, 0, 0}; |
| int col; |
| |
| uint32_t mask = 0; |
| |
| switch (bits) |
| { |
| case 8: |
| mask = 0x0ff; |
| break; |
| case 9: |
| mask = 0x1ff; |
| break; |
| case 10: |
| mask = 0x3ff; |
| break; |
| case 11: |
| mask = 0x7ff; |
| break; |
| case 12: |
| mask = 0xfff; |
| break; |
| default: |
| mask = 0; |
| /* TODO: Shouldn't this be treated as a fatal error? */ |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| break; |
| } |
| |
| for (col = 0; col < (int)ID->columns; col++) |
| { |
| int color; |
| uint32_t val = data[col]; |
| |
| for (color = 0; color < 3; color++) |
| { |
| uint16_t c_fix; |
| c[color] += get_simple_diff(HUF, (val >> (color * bits)) & mask); |
| |
| switch (ID->type_format) |
| { |
| case X3F_IMAGE_RAW_HUFFMAN_X530: |
| case X3F_IMAGE_RAW_HUFFMAN_10BIT: |
| c_fix = (int16_t)c[color] > 0 ? c[color] : 0; |
| |
| HUF->x3rgb16.data[3 * (row * ID->columns + col) + color] = c_fix; |
| break; |
| case X3F_IMAGE_THUMB_HUFFMAN: |
| c_fix = (int8_t)c[color] > 0 ? c[color] : 0; |
| |
| HUF->rgb8.data[3 * (row * ID->columns + col) + color] = uint8_t(c_fix); |
| break; |
| default: |
| /* TODO: Shouldn't this be treated as a fatal error? */ |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| } |
| } |
| } |
| |
| static void simple_decode(x3f_info_t *I, x3f_directory_entry_t *DE, int bits, |
| int row_stride) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| |
| int row; |
| |
| for (row = 0; row < (int)ID->rows; row++) |
| simple_decode_row(I, DE, bits, row, row_stride); |
| } |
| |
| /* --------------------------------------------------------------------- */ |
| /* Loading the data in a directory entry */ |
| /* --------------------------------------------------------------------- */ |
| |
| /* First you set the offset to where to start reading the data ... */ |
| |
| static void read_data_set_offset(x3f_info_t *I, x3f_directory_entry_t *DE, |
| uint32_t header_size) |
| { |
| uint32_t i_off = DE->input.offset + header_size; |
| |
| I->input.file->seek(i_off, SEEK_SET); |
| } |
| |
| /* ... then you read the data, block for block */ |
| |
| static uint32_t read_data_block(void **data, x3f_info_t *I, |
| x3f_directory_entry_t *DE, uint32_t footer) |
| { |
| uint32_t fpos = (I->input.file->tell() & 0x7fffffffU) ; |
| uint32_t size = DE->input.size + DE->input.offset - fpos - footer; |
| |
| if (fpos + size > I->input.file->size()) |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| |
| *data = (void *)x3f_limited_malloc(size); |
| |
| GETN(*data, size); |
| |
| return size; |
| } |
| |
| static uint32_t data_block_size(void ** /*data*/, x3f_info_t *I, |
| x3f_directory_entry_t *DE, uint32_t footer) |
| { |
| uint32_t fpos = (I->input.file->tell() & 0x7fffffffU); |
| uint32_t size = |
| DE->input.size + DE->input.offset - fpos - footer; |
| return size; |
| } |
| |
| static void x3f_load_image_verbatim(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| if (!ID->data_size) |
| ID->data_size = read_data_block(&ID->data, I, DE, 0); |
| } |
| |
| static int32_t x3f_load_image_verbatim_size(x3f_info_t *I, |
| x3f_directory_entry_t *DE) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| return data_block_size(&ID->data, I, DE, 0); |
| } |
| |
| static void x3f_load_property_list(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_property_list_t *PL = &DEH->data_subsection.property_list; |
| int i; |
| |
| read_data_set_offset(I, DE, X3F_PROPERTY_LIST_HEADER_SIZE); |
| |
| GET_PROPERTY_TABLE(PL->property_table, PL->num_properties); |
| |
| if (!PL->data_size) |
| PL->data_size = read_data_block(&PL->data, I, DE, 0); |
| uint32_t maxoffset = PL->data_size / sizeof(utf16_t) - |
| 2; // at least 2 chars, value + terminating 0x0000 |
| |
| for (i = 0; i < (int)PL->num_properties; i++) |
| { |
| x3f_property_t *P = &PL->property_table.element[i]; |
| if (P->name_offset > maxoffset || P->value_offset > maxoffset) |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| P->name = ((utf16_t *)PL->data + P->name_offset); |
| P->value = ((utf16_t *)PL->data + P->value_offset); |
| } |
| } |
| |
| static void x3f_load_true(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| x3f_true_t *TRU = new_true(&ID->tru); |
| x3f_quattro_t *Q = NULL; |
| int i; |
| |
| if (ID->type_format == X3F_IMAGE_RAW_QUATTRO || |
| ID->type_format == X3F_IMAGE_RAW_SDQ || |
| ID->type_format == X3F_IMAGE_RAW_SDQH || |
| ID->type_format == X3F_IMAGE_RAW_SDQH2) |
| { |
| Q = new_quattro(&ID->quattro); |
| |
| for (i = 0; i < TRUE_PLANES; i++) |
| { |
| GET2(Q->plane[i].columns); |
| GET2(Q->plane[i].rows); |
| } |
| |
| if (Q->plane[0].rows == ID->rows / 2) |
| { |
| Q->quattro_layout = 1; |
| } |
| else if (Q->plane[0].rows == ID->rows) |
| { |
| Q->quattro_layout = 0; |
| } |
| else |
| { |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| } |
| |
| /* Read TRUE header data */ |
| GET2(TRU->seed[0]); |
| GET2(TRU->seed[1]); |
| GET2(TRU->seed[2]); |
| GET2(TRU->unknown); |
| GET_TRUE_HUFF_TABLE(TRU->table); |
| |
| if (ID->type_format == X3F_IMAGE_RAW_QUATTRO || |
| ID->type_format == X3F_IMAGE_RAW_SDQ || |
| ID->type_format == X3F_IMAGE_RAW_SDQH || |
| ID->type_format == X3F_IMAGE_RAW_SDQH2) |
| { |
| GET4(Q->unknown); |
| } |
| |
| GET_TABLE(TRU->plane_size, GET4, TRUE_PLANES, uint32_t); |
| |
| /* Read image data */ |
| if (!ID->data_size) |
| ID->data_size = read_data_block(&ID->data, I, DE, 0); |
| |
| /* TODO: can it be fewer than 8 bits? Maybe taken from TRU->table? */ |
| new_huffman_tree(&TRU->tree, 8); |
| |
| populate_true_huffman_tree(&TRU->tree, &TRU->table); |
| |
| #ifdef DBG_PRNT |
| print_huffman_tree(TRU->tree.nodes, 0, 0); |
| #endif |
| |
| TRU->plane_address[0] = (uint8_t *)ID->data; |
| for (i = 1; i < TRUE_PLANES; i++) |
| TRU->plane_address[i] = TRU->plane_address[i - 1] + |
| (((TRU->plane_size.element[i - 1] + 15) / 16) * 16); |
| |
| if ((ID->type_format == X3F_IMAGE_RAW_QUATTRO || |
| ID->type_format == X3F_IMAGE_RAW_SDQ || |
| ID->type_format == X3F_IMAGE_RAW_SDQH || |
| ID->type_format == X3F_IMAGE_RAW_SDQH2) && |
| Q->quattro_layout) |
| { |
| uint32_t columns = Q->plane[0].columns; |
| uint32_t rows = Q->plane[0].rows; |
| uint32_t channels = 3; |
| UINT64 size = UINT64(columns) * UINT64(rows) * UINT64(channels); |
| |
| TRU->x3rgb16.columns = columns; |
| TRU->x3rgb16.rows = rows; |
| TRU->x3rgb16.channels = channels; |
| TRU->x3rgb16.row_stride = columns * channels; |
| TRU->x3rgb16.buf = x3f_limited_malloc(sizeof(uint16_t) * size); |
| TRU->x3rgb16.data = (uint16_t *)TRU->x3rgb16.buf; |
| |
| columns = Q->plane[2].columns; |
| rows = Q->plane[2].rows; |
| channels = 1; |
| size = UINT64(columns) * UINT64(rows) * UINT64(channels); |
| |
| Q->top16.columns = columns; |
| Q->top16.rows = rows; |
| Q->top16.channels = channels; |
| Q->top16.row_stride = columns * channels; |
| Q->top16.buf = x3f_limited_malloc(sizeof(uint16_t) * size); |
| Q->top16.data = (uint16_t *)Q->top16.buf; |
| } |
| else |
| { |
| UINT64 size = UINT64(ID->columns) * UINT64(ID->rows) * 3ULL; |
| |
| TRU->x3rgb16.columns = ID->columns; |
| TRU->x3rgb16.rows = ID->rows; |
| TRU->x3rgb16.channels = 3; |
| TRU->x3rgb16.row_stride = ID->columns * 3; |
| TRU->x3rgb16.buf = x3f_limited_malloc(sizeof(uint16_t) * size); |
| TRU->x3rgb16.data = (uint16_t *)TRU->x3rgb16.buf; |
| } |
| |
| true_decode(I, DE); |
| } |
| |
| static void x3f_load_huffman_compressed(x3f_info_t *I, |
| x3f_directory_entry_t *DE, int bits, |
| int /*use_map_table*/) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| x3f_huffman_t *HUF = ID->huffman; |
| int table_size = 1 << bits; |
| int row_offsets_size = ID->rows * sizeof(HUF->row_offsets.element[0]); |
| |
| GET_TABLE(HUF->table, GET4, table_size, uint32_t); |
| |
| if (!ID->data_size) |
| ID->data_size = read_data_block(&ID->data, I, DE, row_offsets_size); |
| |
| GET_TABLE(HUF->row_offsets, GET4, ID->rows, uint32_t); |
| |
| new_huffman_tree(&HUF->tree, bits); |
| populate_huffman_tree(&HUF->tree, &HUF->table, &HUF->mapping); |
| |
| huffman_decode(I, DE, bits); |
| } |
| |
| static void x3f_load_huffman_not_compressed(x3f_info_t *I, |
| x3f_directory_entry_t *DE, int bits, |
| int /*use_map_table*/, int row_stride) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| |
| if (!ID->data_size) |
| ID->data_size = read_data_block(&ID->data, I, DE, 0); |
| |
| simple_decode(I, DE, bits, row_stride); |
| } |
| |
| static void x3f_load_huffman(x3f_info_t *I, x3f_directory_entry_t *DE, int bits, |
| int use_map_table, int row_stride) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| x3f_huffman_t *HUF = new_huffman(&ID->huffman); |
| UINT64 size; |
| |
| if (use_map_table) |
| { |
| int table_size = 1 << bits; |
| |
| GET_TABLE(HUF->mapping, GET2, table_size, uint16_t); |
| } |
| |
| switch (ID->type_format) |
| { |
| case X3F_IMAGE_RAW_HUFFMAN_X530: |
| case X3F_IMAGE_RAW_HUFFMAN_10BIT: |
| size = UINT64(ID->columns) * UINT64(ID->rows) * 3ULL; |
| HUF->x3rgb16.columns = ID->columns; |
| HUF->x3rgb16.rows = ID->rows; |
| HUF->x3rgb16.channels = 3; |
| HUF->x3rgb16.row_stride = ID->columns * 3; |
| HUF->x3rgb16.buf = x3f_limited_malloc(sizeof(uint16_t) * size); |
| HUF->x3rgb16.data = (uint16_t *)HUF->x3rgb16.buf; |
| break; |
| case X3F_IMAGE_THUMB_HUFFMAN: |
| size = UINT64(ID->columns) * UINT64(ID->rows) * 3ULL; |
| HUF->rgb8.columns = ID->columns; |
| HUF->rgb8.rows = ID->rows; |
| HUF->rgb8.channels = 3; |
| HUF->rgb8.row_stride = ID->columns * 3; |
| HUF->rgb8.buf = x3f_limited_malloc(sizeof(uint8_t) * size); |
| HUF->rgb8.data = (uint8_t *)HUF->rgb8.buf; |
| break; |
| default: |
| /* TODO: Shouldn't this be treated as a fatal error? */ |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| |
| if (row_stride == 0) |
| return x3f_load_huffman_compressed(I, DE, bits, use_map_table); |
| else |
| return x3f_load_huffman_not_compressed(I, DE, bits, use_map_table, |
| row_stride); |
| } |
| |
| static void x3f_load_pixmap(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| x3f_load_image_verbatim(I, DE); |
| } |
| |
| static int32_t x3f_load_pixmap_size(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| return x3f_load_image_verbatim_size(I, DE); |
| } |
| |
| static void x3f_load_jpeg(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| x3f_load_image_verbatim(I, DE); |
| } |
| |
| static int32_t x3f_load_jpeg_size(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| return x3f_load_image_verbatim_size(I, DE); |
| } |
| |
| static void x3f_load_image(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| |
| if (ID->rows > 65535 || ID->columns > 65535) |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| |
| read_data_set_offset(I, DE, X3F_IMAGE_HEADER_SIZE); |
| |
| switch (ID->type_format) |
| { |
| case X3F_IMAGE_RAW_TRUE: |
| case X3F_IMAGE_RAW_MERRILL: |
| case X3F_IMAGE_RAW_QUATTRO: |
| case X3F_IMAGE_RAW_SDQ: |
| case X3F_IMAGE_RAW_SDQH: |
| case X3F_IMAGE_RAW_SDQH2: |
| x3f_load_true(I, DE); |
| break; |
| case X3F_IMAGE_RAW_HUFFMAN_X530: |
| case X3F_IMAGE_RAW_HUFFMAN_10BIT: |
| x3f_load_huffman(I, DE, 10, 1, ID->row_stride); |
| break; |
| case X3F_IMAGE_THUMB_PLAIN: |
| x3f_load_pixmap(I, DE); |
| break; |
| case X3F_IMAGE_THUMB_HUFFMAN: |
| x3f_load_huffman(I, DE, 8, 0, ID->row_stride); |
| break; |
| case X3F_IMAGE_THUMB_JPEG: |
| x3f_load_jpeg(I, DE); |
| break; |
| default: |
| /* TODO: Shouldn't this be treated as a fatal error? */ |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| } |
| |
| // Used only for thumbnail size estimation |
| static int32_t x3f_load_image_size(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_image_data_t *ID = &DEH->data_subsection.image_data; |
| |
| read_data_set_offset(I, DE, X3F_IMAGE_HEADER_SIZE); |
| |
| switch (ID->type_format) |
| { |
| case X3F_IMAGE_THUMB_PLAIN: |
| return x3f_load_pixmap_size(I, DE); |
| case X3F_IMAGE_THUMB_JPEG: |
| return x3f_load_jpeg_size(I, DE); |
| break; |
| default: |
| return 0; |
| } |
| } |
| |
| static void x3f_load_camf_decode_type2(x3f_camf_t *CAMF) |
| { |
| uint32_t key = CAMF->t2.crypt_key; |
| int i; |
| |
| CAMF->decoded_data_size = CAMF->data_size; |
| CAMF->decoded_data = x3f_limited_malloc(CAMF->decoded_data_size); |
| |
| for (i = 0; i < (int)CAMF->data_size; i++) |
| { |
| uint8_t old, _new; |
| uint32_t tmp; |
| |
| old = ((uint8_t *)CAMF->data)[i]; |
| key = (key * 1597 + 51749) % 244944; |
| tmp = (uint32_t)(key * ((int64_t)301593171) >> 24); |
| _new = (uint8_t)(old ^ (uint8_t)(((((key << 8) - tmp) >> 1) + tmp) >> 17)); |
| ((uint8_t *)CAMF->decoded_data)[i] = _new; |
| } |
| } |
| |
| /* NOTE: the unpacking in this code is in big respects identical to |
| true_decode_one_color(). The difference is in the output you |
| build. It might be possible to make some parts shared. NOTE ALSO: |
| This means that the meta data is obfuscated using an image |
| compression algorithm. */ |
| |
| static void camf_decode_type4(x3f_camf_t *CAMF) |
| { |
| uint32_t seed = CAMF->t4.decode_bias; |
| int row; |
| |
| uint8_t *dst; |
| uint32_t dst_size = CAMF->t4.decoded_data_size; |
| uint8_t *dst_end; |
| |
| bool_t odd_dst = 0; |
| |
| x3f_hufftree_t *tree = &CAMF->tree; |
| bit_state_t BS; |
| |
| int32_t row_start_acc[2][2]; |
| uint32_t rows = CAMF->t4.block_count; |
| uint32_t cols = CAMF->t4.block_size; |
| |
| CAMF->decoded_data_size = dst_size; |
| |
| CAMF->decoded_data = x3f_limited_malloc(CAMF->decoded_data_size); |
| memset(CAMF->decoded_data, 0, CAMF->decoded_data_size); |
| |
| dst = (uint8_t *)CAMF->decoded_data; |
| dst_end = dst + dst_size; |
| |
| set_bit_state(&BS, CAMF->decoding_start); |
| |
| row_start_acc[0][0] = seed; |
| row_start_acc[0][1] = seed; |
| row_start_acc[1][0] = seed; |
| row_start_acc[1][1] = seed; |
| |
| for (row = 0; row < (int)rows; row++) |
| { |
| int col; |
| bool_t odd_row = row & 1; |
| int32_t acc[2]; |
| |
| /* We loop through all the columns and the rows. But the actual |
| data is smaller than that, so we break the loop when reaching |
| the end. */ |
| for (col = 0; col < (int)cols; col++) |
| { |
| bool_t odd_col = col & 1; |
| int32_t diff = get_true_diff(&BS, tree); |
| int32_t prev = col < 2 ? row_start_acc[odd_row][odd_col] : acc[odd_col]; |
| int32_t value = prev + diff; |
| |
| acc[odd_col] = value; |
| if (col < 2) |
| row_start_acc[odd_row][odd_col] = value; |
| |
| switch (odd_dst) |
| { |
| case 0: |
| *dst++ = (uint8_t)((value >> 4) & 0xff); |
| |
| if (dst >= dst_end) |
| { |
| goto ready; |
| } |
| |
| *dst = (uint8_t)((value << 4) & 0xf0); |
| break; |
| case 1: |
| *dst++ |= (uint8_t)((value >> 8) & 0x0f); |
| |
| if (dst >= dst_end) |
| { |
| goto ready; |
| } |
| |
| *dst++ = (uint8_t)((value << 0) & 0xff); |
| |
| if (dst >= dst_end) |
| { |
| goto ready; |
| } |
| |
| break; |
| } |
| |
| odd_dst = !odd_dst; |
| } /* end col */ |
| } /* end row */ |
| |
| ready:; |
| } |
| |
| static void x3f_load_camf_decode_type4(x3f_camf_t *CAMF) |
| { |
| int i; |
| uint8_t *p; |
| x3f_true_huffman_element_t *element = NULL; |
| |
| for (i = 0, p = (uint8_t *)CAMF->data; *p != 0; i++) |
| { |
| /* TODO: Is this too expensive ??*/ |
| element = (x3f_true_huffman_element_t *)x3f_limited_realloc(element, |
| (i + 1) * sizeof(*element)); |
| |
| element[i].code_size = *p++; |
| element[i].code = *p++; |
| } |
| |
| CAMF->table.size = i; |
| CAMF->table.element = element; |
| |
| /* TODO: where does the values 28 and 32 come from? */ |
| #define CAMF_T4_DATA_SIZE_OFFSET 28 |
| #define CAMF_T4_DATA_OFFSET 32 |
| CAMF->decoding_size = |
| *(uint32_t *)((unsigned char *)CAMF->data + CAMF_T4_DATA_SIZE_OFFSET); |
| CAMF->decoding_start = (uint8_t *)CAMF->data + CAMF_T4_DATA_OFFSET; |
| |
| /* TODO: can it be fewer than 8 bits? Maybe taken from TRU->table? */ |
| new_huffman_tree(&CAMF->tree, 8); |
| |
| populate_true_huffman_tree(&CAMF->tree, &CAMF->table); |
| |
| #ifdef DBG_PRNT |
| print_huffman_tree(CAMF->tree.nodes, 0, 0); |
| #endif |
| |
| camf_decode_type4(CAMF); |
| } |
| |
| static void camf_decode_type5(x3f_camf_t *CAMF) |
| { |
| int32_t acc = CAMF->t5.decode_bias; |
| |
| uint8_t *dst; |
| |
| x3f_hufftree_t *tree = &CAMF->tree; |
| bit_state_t BS; |
| |
| int32_t i; |
| |
| CAMF->decoded_data_size = CAMF->t5.decoded_data_size; |
| CAMF->decoded_data = x3f_limited_malloc(CAMF->decoded_data_size); |
| |
| dst = (uint8_t *)CAMF->decoded_data; |
| |
| set_bit_state(&BS, CAMF->decoding_start); |
| |
| for (i = 0; i < (int)CAMF->decoded_data_size; i++) |
| { |
| int32_t diff = get_true_diff(&BS, tree); |
| |
| acc = acc + diff; |
| *dst++ = (uint8_t)(acc & 0xff); |
| } |
| } |
| |
| static void x3f_load_camf_decode_type5(x3f_camf_t *CAMF) |
| { |
| int i; |
| uint8_t *p; |
| x3f_true_huffman_element_t *element = NULL; |
| |
| for (i = 0, p = (uint8_t *)CAMF->data; *p != 0; i++) |
| { |
| /* TODO: Is this too expensive ??*/ |
| element = (x3f_true_huffman_element_t *)x3f_limited_realloc(element, |
| (i + 1) * sizeof(*element)); |
| |
| element[i].code_size = *p++; |
| element[i].code = *p++; |
| } |
| |
| CAMF->table.size = i; |
| CAMF->table.element = element; |
| |
| /* TODO: where does the values 28 and 32 come from? */ |
| #define CAMF_T5_DATA_SIZE_OFFSET 28 |
| #define CAMF_T5_DATA_OFFSET 32 |
| CAMF->decoding_size = |
| *(uint32_t *)((uint8_t *)CAMF->data + CAMF_T5_DATA_SIZE_OFFSET); |
| CAMF->decoding_start = (uint8_t *)CAMF->data + CAMF_T5_DATA_OFFSET; |
| |
| /* TODO: can it be fewer than 8 bits? Maybe taken from TRU->table? */ |
| new_huffman_tree(&CAMF->tree, 8); |
| |
| populate_true_huffman_tree(&CAMF->tree, &CAMF->table); |
| |
| #ifdef DBG_PRNT |
| print_huffman_tree(CAMF->tree.nodes, 0, 0); |
| #endif |
| |
| camf_decode_type5(CAMF); |
| } |
| |
| static void x3f_setup_camf_text_entry(camf_entry_t *entry) |
| { |
| entry->text_size = *(uint32_t *)entry->value_address; |
| entry->text = (char *)entry->value_address + 4; |
| } |
| |
| static void x3f_setup_camf_property_entry(camf_entry_t *entry) |
| { |
| int i; |
| uint8_t *e = (uint8_t *)entry->entry; |
| uint8_t *v = (uint8_t *)entry->value_address; |
| uint32_t num = entry->property_num = *(uint32_t *)v; |
| uint32_t off = *(uint32_t *)(v + 4); |
| |
| entry->property_name = (char **)x3f_limited_malloc(num * sizeof(uint8_t *)); |
| entry->property_value = (uint8_t **)x3f_limited_malloc(num * sizeof(uint8_t *)); |
| |
| for (i = 0; i < (int)num; i++) |
| { |
| uint32_t name_off = off + *(uint32_t *)(v + 8 + 8 * i); |
| uint32_t value_off = off + *(uint32_t *)(v + 8 + 8 * i + 4); |
| |
| entry->property_name[i] = (char *)(e + name_off); |
| entry->property_value[i] = e + value_off; |
| } |
| } |
| |
| static void set_matrix_element_info(uint32_t type, uint32_t *size, |
| matrix_type_t *decoded_type) |
| { |
| switch (type) |
| { |
| case 0: |
| *size = 2; |
| *decoded_type = M_INT; /* known to be true */ |
| break; |
| case 1: |
| *size = 4; |
| *decoded_type = M_UINT; /* TODO: unknown ???? */ |
| break; |
| case 2: |
| *size = 4; |
| *decoded_type = M_UINT; /* TODO: unknown ???? */ |
| break; |
| case 3: |
| *size = 4; |
| *decoded_type = M_FLOAT; /* known to be true */ |
| break; |
| case 5: |
| *size = 1; |
| *decoded_type = M_UINT; /* TODO: unknown ???? */ |
| break; |
| case 6: |
| *size = 2; |
| *decoded_type = M_UINT; /* TODO: unknown ???? */ |
| break; |
| default: |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| } |
| |
| static void get_matrix_copy(camf_entry_t *entry) |
| { |
| uint32_t element_size = entry->matrix_element_size; |
| uint32_t elements = entry->matrix_elements; |
| int i, size = (entry->matrix_decoded_type == M_FLOAT ? sizeof(double) |
| : sizeof(uint32_t)) * |
| elements; |
| |
| entry->matrix_decoded = x3f_limited_malloc(size); |
| |
| switch (element_size) |
| { |
| case 4: |
| switch (entry->matrix_decoded_type) |
| { |
| case M_INT: |
| case M_UINT: |
| memcpy(entry->matrix_decoded, entry->matrix_data, size); |
| break; |
| case M_FLOAT: |
| for (i = 0; i < (int)elements; i++) |
| ((double *)entry->matrix_decoded)[i] = |
| (double)((float *)entry->matrix_data)[i]; |
| break; |
| default: |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| break; |
| case 2: |
| switch (entry->matrix_decoded_type) |
| { |
| case M_INT: |
| for (i = 0; i < (int)elements; i++) |
| ((int32_t *)entry->matrix_decoded)[i] = |
| (int32_t)((int16_t *)entry->matrix_data)[i]; |
| break; |
| case M_UINT: |
| for (i = 0; i < (int)elements; i++) |
| ((uint32_t *)entry->matrix_decoded)[i] = |
| (uint32_t)((uint16_t *)entry->matrix_data)[i]; |
| break; |
| default: |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| break; |
| case 1: |
| switch (entry->matrix_decoded_type) |
| { |
| case M_INT: |
| for (i = 0; i < (int)elements; i++) |
| ((int32_t *)entry->matrix_decoded)[i] = |
| (int32_t)((int8_t *)entry->matrix_data)[i]; |
| break; |
| case M_UINT: |
| for (i = 0; i < (int)elements; i++) |
| ((uint32_t *)entry->matrix_decoded)[i] = |
| (uint32_t)((uint8_t *)entry->matrix_data)[i]; |
| break; |
| default: |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| break; |
| default: |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| } |
| |
| static void x3f_setup_camf_matrix_entry(camf_entry_t *entry) |
| { |
| int i; |
| int totalsize = 1; |
| |
| uint8_t *e = (uint8_t *)entry->entry; |
| uint8_t *v = (uint8_t *)entry->value_address; |
| uint32_t type = entry->matrix_type = *(uint32_t *)(v + 0); |
| uint32_t dim = entry->matrix_dim = *(uint32_t *)(v + 4); |
| uint32_t off = entry->matrix_data_off = *(uint32_t *)(v + 8); |
| camf_dim_entry_t *dentry = entry->matrix_dim_entry = |
| (camf_dim_entry_t *)x3f_limited_malloc(dim * sizeof(camf_dim_entry_t)); |
| |
| for (i = 0; i < (int)dim; i++) |
| { |
| uint32_t size = dentry[i].size = *(uint32_t *)(v + 12 + 12 * i + 0); |
| dentry[i].name_offset = *(uint32_t *)(v + 12 + 12 * i + 4); |
| dentry[i].n = *(uint32_t *)(v + 12 + 12 * i + 8); |
| dentry[i].name = (char *)(e + dentry[i].name_offset); |
| |
| if ((int)dentry[i].n != i) |
| { |
| } |
| |
| totalsize *= size; |
| } |
| |
| set_matrix_element_info(type, &entry->matrix_element_size, |
| &entry->matrix_decoded_type); |
| entry->matrix_data = (void *)(e + off); |
| |
| entry->matrix_elements = totalsize; |
| entry->matrix_used_space = entry->entry_size - off; |
| |
| /* This estimate only works for matrices above a certain size */ |
| entry->matrix_estimated_element_size = entry->matrix_used_space / totalsize; |
| |
| get_matrix_copy(entry); |
| } |
| |
| static void x3f_setup_camf_entries(x3f_camf_t *CAMF) |
| { |
| uint8_t *p = (uint8_t *)CAMF->decoded_data; |
| uint8_t *end = p + CAMF->decoded_data_size; |
| camf_entry_t *entry = NULL; |
| int i; |
| |
| for (i = 0; p < end; i++) |
| { |
| uint32_t *p4 = (uint32_t *)p; |
| |
| switch (*p4) |
| { |
| case X3F_CMbP: |
| case X3F_CMbT: |
| case X3F_CMbM: |
| break; |
| default: |
| goto stop; |
| } |
| |
| /* TODO: lots of realloc - may be inefficient */ |
| entry = (camf_entry_t *)x3f_limited_realloc(entry, (i + 1) * sizeof(camf_entry_t)); |
| |
| /* Pointer */ |
| entry[i].entry = p; |
| |
| /* Header */ |
| entry[i].id = *p4++; |
| entry[i].version = *p4++; |
| entry[i].entry_size = *p4++; |
| entry[i].name_offset = *p4++; |
| entry[i].value_offset = *p4++; |
| |
| /* Compute addresses and sizes */ |
| entry[i].name_address = (char *)(p + entry[i].name_offset); |
| entry[i].value_address = p + entry[i].value_offset; |
| entry[i].name_size = entry[i].value_offset - entry[i].name_offset; |
| entry[i].value_size = entry[i].entry_size - entry[i].value_offset; |
| |
| entry[i].text_size = 0; |
| entry[i].text = NULL; |
| entry[i].property_num = 0; |
| entry[i].property_name = NULL; |
| entry[i].property_value = NULL; |
| entry[i].matrix_type = 0; |
| entry[i].matrix_dim = 0; |
| entry[i].matrix_data_off = 0; |
| entry[i].matrix_data = NULL; |
| entry[i].matrix_dim_entry = NULL; |
| |
| entry[i].matrix_decoded = NULL; |
| |
| switch (entry[i].id) |
| { |
| case X3F_CMbP: |
| x3f_setup_camf_property_entry(&entry[i]); |
| break; |
| case X3F_CMbT: |
| x3f_setup_camf_text_entry(&entry[i]); |
| break; |
| case X3F_CMbM: |
| x3f_setup_camf_matrix_entry(&entry[i]); |
| break; |
| } |
| |
| p += entry[i].entry_size; |
| } |
| |
| stop: |
| |
| CAMF->entry_table.size = i; |
| CAMF->entry_table.element = entry; |
| } |
| |
| static void x3f_load_camf(x3f_info_t *I, x3f_directory_entry_t *DE) |
| { |
| x3f_directory_entry_header_t *DEH = &DE->header; |
| x3f_camf_t *CAMF = &DEH->data_subsection.camf; |
| |
| read_data_set_offset(I, DE, X3F_CAMF_HEADER_SIZE); |
| |
| if (!CAMF->data_size) |
| CAMF->data_size = read_data_block(&CAMF->data, I, DE, 0); |
| |
| switch (CAMF->type) |
| { |
| case 2: /* Older SD9-SD14 */ |
| x3f_load_camf_decode_type2(CAMF); |
| break; |
| case 4: /* TRUE ... Merrill */ |
| x3f_load_camf_decode_type4(CAMF); |
| break; |
| case 5: /* Quattro ... */ |
| x3f_load_camf_decode_type5(CAMF); |
| break; |
| default: |
| /* TODO: Shouldn't this be treated as a fatal error? */ |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| |
| if (CAMF->decoded_data != NULL) |
| x3f_setup_camf_entries(CAMF); |
| else |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| } |
| |
| /* extern */ x3f_return_t x3f_load_data(x3f_t *x3f, x3f_directory_entry_t *DE) |
| { |
| x3f_info_t *I = &x3f->info; |
| |
| if (DE == NULL) |
| return X3F_ARGUMENT_ERROR; |
| |
| switch (DE->header.identifier) |
| { |
| case X3F_SECp: |
| x3f_load_property_list(I, DE); |
| break; |
| case X3F_SECi: |
| x3f_load_image(I, DE); |
| break; |
| case X3F_SECc: |
| x3f_load_camf(I, DE); |
| break; |
| default: |
| return X3F_INTERNAL_ERROR; |
| } |
| return X3F_OK; |
| } |
| |
| /* extern */ int32_t x3f_load_data_size(x3f_t *x3f, x3f_directory_entry_t *DE) |
| { |
| x3f_info_t *I = &x3f->info; |
| |
| if (DE == NULL) |
| return -1; |
| |
| switch (DE->header.identifier) |
| { |
| case X3F_SECi: |
| return x3f_load_image_size(I, DE); |
| default: |
| return 0; |
| } |
| } |
| |
| /* extern */ x3f_return_t x3f_load_image_block(x3f_t *x3f, |
| x3f_directory_entry_t *DE) |
| { |
| x3f_info_t *I = &x3f->info; |
| |
| if (DE == NULL) |
| return X3F_ARGUMENT_ERROR; |
| |
| switch (DE->header.identifier) |
| { |
| case X3F_SECi: |
| read_data_set_offset(I, DE, X3F_IMAGE_HEADER_SIZE); |
| x3f_load_image_verbatim(I, DE); |
| break; |
| default: |
| throw LIBRAW_EXCEPTION_IO_CORRUPT; |
| return X3F_INTERNAL_ERROR; /* unreachable code*/ |
| } |
| |
| return X3F_OK; |
| } |
| |
| /* --------------------------------------------------------------------- */ |
| /* The End */ |
| /* --------------------------------------------------------------------- */ |
| |
| #endif |