-
Notifications
You must be signed in to change notification settings - Fork 11
Fix encapsulated pixeldata handling #103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 6 commits
87c0436
835ed96
d190231
0ceb9cc
c02f192
7a08c78
ab8b9f0
ac71ff4
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -954,56 +954,80 @@ bool dcm_parse_pixeldata_offsets(DcmError **error, | |
| // the BOT is missing, we must scan pixeldata to find the position of | ||
| // each frame | ||
|
|
||
| // we could use our generic parser above ^^ but we have a special loop | ||
| // here as an optimisation (we can skip over the pixel data itself) | ||
|
|
||
| dcm_log_info("building Offset Table from Pixel Data"); | ||
|
|
||
| // 0 in the BOT is the offset to the start of frame 1, ie. here | ||
| *first_frame_offset = position; | ||
|
|
||
| position = 0; | ||
| for (int i = 0; i < num_frames; i++) { | ||
| if (!read_tag(&state, &tag, &position) || | ||
| !read_uint32(&state, &length, &position)) { | ||
| return false; | ||
| // each frame may consist of several fragments, so we need to scan each fragment to find the next frame | ||
| if (!read_tag(&state, &tag, &position)) | ||
| { | ||
| dcm_error_set(error, DCM_ERROR_CODE_PARSE, | ||
| "building BasicOffsetTable failed", | ||
| "failed to read tag"); | ||
| return false; | ||
| } | ||
| // all fragments belong to the only frame | ||
| if ( num_frames == 1 ) | ||
| { | ||
| // according to the standard the first frame's offset is 0 | ||
| offsets[0] = 0; | ||
| } | ||
| else | ||
| { | ||
| // 1 fragment shall contain 1 frame | ||
| int fragment_idx = 0; | ||
| offsets[fragment_idx] = 0; // by definition the first fragment is at offset 0 | ||
| fragment_idx++; | ||
| while( tag != TAG_SQ_DELIM ) | ||
| { | ||
| if ( tag != TAG_ITEM || !read_uint32(&state, &length, &position)) | ||
| { | ||
| dcm_error_set(error, DCM_ERROR_CODE_PARSE, | ||
| "building BasicOffsetTable failed", | ||
| "failed to read fragment"); | ||
| return false; | ||
| } | ||
| // skip actual content to find next offset | ||
| dcm_seekcur(&state, length, &position); | ||
| if (!read_tag(&state, &tag, &position)) | ||
| { | ||
| dcm_error_set(error, DCM_ERROR_CODE_PARSE, | ||
| "building BasicOffsetTable failed", | ||
| "failed to read tag"); | ||
| return false; | ||
| } | ||
| if ( fragment_idx < num_frames ) | ||
| { | ||
| offsets[fragment_idx] = offsets[fragment_idx-1] + 8/*tag and length field size*/ + length; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure I understand. I think you can have many fragments in each frame, so don't you need two loops here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This part is a kind of an assumption. It may be according to the standard, but the standard is not an easy reading. |
||
| } | ||
| fragment_idx++; | ||
| } | ||
|
|
||
| if (tag == TAG_SQ_DELIM) { | ||
| dcm_error_set(error, DCM_ERROR_CODE_PARSE, | ||
| "reading BasicOffsetTable failed", | ||
| "too few frames in PixelData"); | ||
| // fragment_idx shall equal to num_frames+1 at the end | ||
| fragment_idx--; | ||
| if ( fragment_idx > num_frames ) | ||
| { | ||
| dcm_error_set(error, DCM_ERROR_CODE_INVALID, | ||
| "building BasicOffsetTable failed", | ||
| "Too many fragments" ); | ||
| return false; | ||
| } | ||
|
|
||
| if (tag != TAG_ITEM) { | ||
| dcm_error_set(error, DCM_ERROR_CODE_PARSE, | ||
| if ( num_frames < fragment_idx ) | ||
| { | ||
| dcm_error_set(error, DCM_ERROR_CODE_INVALID, | ||
| "building BasicOffsetTable failed", | ||
| "frame Item #%d has wrong tag '%08x'", | ||
| i + 1, | ||
| tag); | ||
| "Too many frames" ); | ||
| return false; | ||
| } | ||
|
|
||
| // step back to the start of the item for this frame | ||
| offsets[i] = position - 8; | ||
|
|
||
| // and seek forward over the value | ||
| if (!dcm_seekcur(&state, length, &position)) { | ||
| if ( !read_uint32(&state, &length, &position ) || length != 0 ) | ||
| { | ||
| dcm_error_set(error, DCM_ERROR_CODE_PARSE, | ||
| "building BasicOffsetTable failed", | ||
| "Sequence Delimiter Tag failure" ); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| // the next thing should be the end of sequence tag | ||
| if (!read_tag(&state, &tag, &position)) { | ||
| return false; | ||
| } | ||
| if (tag != TAG_SQ_DELIM) { | ||
| dcm_error_set(error, DCM_ERROR_CODE_PARSE, | ||
| "reading BasicOffsetTable failed", | ||
| "too many frames in PixelData"); | ||
| return false; | ||
| } | ||
| } | ||
|
|
||
| return true; | ||
|
|
@@ -1022,34 +1046,82 @@ char *dcm_parse_frame(DcmError **error, | |
| .big_endian = is_big_endian(), | ||
| }; | ||
|
|
||
| const uint8_t bytes_per_pixel = desc->bits_allocated/8; | ||
|
||
| *length = desc->rows * desc->columns * desc->samples_per_pixel * bytes_per_pixel; | ||
|
|
||
| char *value = DCM_MALLOC(error, *length); | ||
| if (value == NULL) { | ||
| return NULL; | ||
| } | ||
| int64_t position = 0; | ||
| if (!dcm_require(&state, value, *length, &position)) { | ||
| free(value); | ||
| return NULL; | ||
| } | ||
|
|
||
| if (dcm_is_encapsulated_transfer_syntax(desc->transfer_syntax_uid)) { | ||
| uint32_t tag; | ||
| if (!read_tag(&state, &tag, &position) || | ||
| !read_uint32(&state, length, &position)) { | ||
| return NULL; | ||
| } | ||
| return value; | ||
| } | ||
|
|
||
| if (tag != TAG_ITEM) { | ||
| char *dcm_parse_encapsulated_frame(DcmError **error, | ||
| DcmIO *io, | ||
| bool implicit, | ||
| int64_t frame_end_offset, | ||
| uint32_t* length) | ||
| { | ||
| DcmParseState state = { | ||
| .error = error, | ||
| .io = io, | ||
| .implicit = implicit, | ||
| .big_endian = is_big_endian(), | ||
| }; | ||
|
|
||
| int64_t position = 0; | ||
| *length = 0; | ||
| uint32_t tag; | ||
| if ( !read_tag(&state, &tag, &position) ) | ||
| { | ||
| return NULL; | ||
| } | ||
| uint32_t fragment_length = 0; | ||
| // first determine the total length of bytes to be read | ||
| while( position < frame_end_offset && tag != TAG_SQ_DELIM ) | ||
|
||
| { | ||
| if (tag != TAG_ITEM) | ||
| { | ||
| dcm_error_set(error, DCM_ERROR_CODE_PARSE, | ||
| "reading frame item failed", | ||
| "no item tag found for frame item"); | ||
| return NULL; | ||
| } | ||
| } else { | ||
| *length = desc->rows * desc->columns * desc->samples_per_pixel * | ||
| (desc->bits_allocated / 8); | ||
| if (!read_uint32(&state, &fragment_length, &position)) | ||
| { | ||
| return NULL; | ||
| } | ||
| dcm_seekcur(&state, fragment_length, &position); | ||
| *length += fragment_length; | ||
| if (!read_tag(&state, &tag, &position)) | ||
| { | ||
| return NULL; | ||
| } | ||
| } | ||
|
|
||
| char *value = DCM_MALLOC(error, *length); | ||
| if (value == NULL) { | ||
| if (value == NULL) | ||
| { | ||
| return NULL; | ||
| } | ||
| if (!dcm_require(&state, value, *length, &position)) { | ||
| free(value); | ||
| return NULL; | ||
| // reposition to the beginning of encapsulated pixel data | ||
| dcm_seekcur(&state, -position, &position); | ||
|
|
||
| fragment_length = 0; | ||
| char* fragment = value; | ||
| position = 0; | ||
| read_tag(&state, &tag, &position); | ||
| while( position < frame_end_offset && tag != TAG_SQ_DELIM ) | ||
| { | ||
| read_uint32(&state, &fragment_length, &position); | ||
| dcm_read(&state, fragment, fragment_length, &position ); | ||
|
||
| fragment += fragment_length; | ||
| read_tag(&state, &tag, &position); | ||
| } | ||
|
|
||
| return value; | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a getter for this, I would write: