Skip to content
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

Return an error if Pixel Representation is signed and a signed native is pixel data value is found. #296

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
52 changes: 46 additions & 6 deletions read.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,10 @@ var (
// ErrorUnsupportedBitsAllocated indicates that the BitsAllocated in the
// NativeFrame PixelData is unsupported. In this situation, the rest of the
// dataset returned is still valid.
ErrorUnsupportedBitsAllocated = errors.New("unsupported BitsAllocated")
errorUnableToParseFloat = errors.New("unable to parse float type")
ErrorExpectedEvenLength = errors.New("field length is not even, in violation of DICOM spec")
ErrorUnsupportedBitsAllocated = errors.New("unsupported BitsAllocated")
errorUnableToParseFloat = errors.New("unable to parse float type")
ErrorExpectedEvenLength = errors.New("field length is not even, in violation of DICOM spec")
ErrorSignedNativePixelDataUnsupported = errors.New("the Pixel Representation tag indicates signed native pixel data is present, _and_ negative Pixel Data values were found, but this is not yet supported")
)

// reader is responsible for mid-level dicom parsing capabilities, like
Expand Down Expand Up @@ -368,6 +369,17 @@ func makeErrorPixelData(reader io.Reader, vl uint32, fc chan<- *frame.Frame, par
func (r *reader) readNativeFrames(parsedData *Dataset, fc chan<- *frame.Frame, vl uint32) (pixelData *PixelDataInfo,
bytesToRead int, err error) {
// Parse information from previously parsed attributes that are needed to parse NativeData Frames:

// TODO(https://github.com/suyashkumar/dicom/issues/294): Add support for
// signed Native PixelData.
pixelDataIsSigned := false
if pxRep, err := parsedData.FindElementByTag(tag.PixelRepresentation); err == nil {
pxRepValue := MustGetInts(pxRep.Value)
if len(pxRepValue) > 0 && pxRepValue[0] != 0 {
pixelDataIsSigned = true
}
}

rows, err := parsedData.FindElementByTag(tag.Rows)
if err != nil {
return nil, 0, err
Expand Down Expand Up @@ -469,12 +481,24 @@ func (r *reader) readNativeFrames(parsedData *Dataset, fc chan<- *frame.Frame, v
return nil, bytesToRead,
fmt.Errorf("could not read uint%d from input: %w", bitsAllocated, err)
}
insertIdx := (pixel * samplesPerPixel) + value
if bitsAllocated == 8 {
buf[(pixel*samplesPerPixel)+value] = int(pixelBuf[0])
buf[insertIdx] = int(pixelBuf[0])
if pixelDataIsSigned && isNegativeIn2sComplement(pixelBuf[0]) {
return nil, bytesToRead, ErrorSignedNativePixelDataUnsupported
}
} else if bitsAllocated == 16 {
buf[(pixel*samplesPerPixel)+value] = int(bo.Uint16(pixelBuf))
val := bo.Uint16(pixelBuf)
buf[insertIdx] = int(val)
if pixelDataIsSigned && isNegativeIn2sComplement(val) {
return nil, bytesToRead, ErrorSignedNativePixelDataUnsupported
}
} else if bitsAllocated == 32 {
buf[(pixel*samplesPerPixel)+value] = int(bo.Uint32(pixelBuf))
val := bo.Uint32(pixelBuf)
buf[insertIdx] = int(val)
if pixelDataIsSigned && isNegativeIn2sComplement(val) {
return nil, bytesToRead, ErrorSignedNativePixelDataUnsupported
}
} else {
return nil, bytesToRead, fmt.Errorf("bitsAllocated=%d : %w", bitsAllocated, ErrorUnsupportedBitsAllocated)
}
Expand Down Expand Up @@ -829,3 +853,19 @@ func (r *reader) readRawItem(shouldSkip bool) ([]byte, bool, error) {
func (r *reader) moreToRead() bool {
return !r.rawReader.IsLimitExhausted()
}

// isNegativeIn2sComplement returns true if the most significant bit is 1 of the
// input unsigned integer. Panics if the input is not a Go unsigned integer.
func isNegativeIn2sComplement(input any) bool {
switch v := input.(type) {
case uint8:
return (1 << 7 & v) > 0
case uint16:
return (1 << 15 & v) > 0
case uint32:
return (1 << 31 & v) > 0
case uint64:
return (1 << 63 & v) > 0
}
panic("isNegativeIn2sComplement expects some form of Go unsigned integer")
}
Loading