// dpxReader.C // Copyright (c) 2009 The Foundry Visionmongers Ltd. All Rights Reserved. #include "DDImage/FileReader.h" #include "DDImage/Row.h" #include "DDImage/DDMath.h" #include "DDImage/Knob.h" #include "DDImage/ARRAY.h" #include "DDImage/Matrix3.h" #include "DPXimage.h" #include #include #include #include #include using namespace DD::Image; const float Kb = .0722f; //.114f; const float Kr = .2126f; //.299f; ///! Sanitizes the string by replacing any non-printable characters with ///! C-style escape sequences. inline std::string SanitizeString(const std::string& s) { std::ostringstream oss; for (size_t i = 0; i < s.length(); i++) { if (isprint(s[i])) oss << s[i]; else // double-cast here is intentional to (a) keep character in range 0-255 rather than -128-127 and // (b) then to print it to the stream as a number rather than as a char oss << "\\x" << std::setw(2) << std::hex << std::setfill('0') << (unsigned int)((unsigned char)s[i]); } return oss.str(); } class dpxReader : public FileReader { // this is the parts of the header we keep: bool flipped; bool ycbcr_hack; unsigned orientation; unsigned width; unsigned height; struct Element { U8 descriptor; U8 bits; U16 packing; U32 dataOffset; U32 bytes; // bytes per line int components; // descriptor decoded into # of components ChannelSet channels; // which Nuke channels it supplies } element[8]; static const Description description; MetaData::Bundle meta; public: const MetaData::Bundle& fetchMetaData(const char* key) { return meta; } dpxReader(Read* iop, int fd, const unsigned char* block, int len) : FileReader(iop, fd, block, len) { DPXHeader header; // Copy the header chunk raw: read(&header, 0, int(sizeof(header))); // Put the header into native-endianess: if (header.file.magicNumber != DPX_MAGIC) { flipped = true; flip(&header.file.magicNumber, 2); // magicNumber & offsetToImageData flip(&header.file.totalFileSize, 5); // totalFileSize thru userDataSize flip(&header.image.orientation, 2); // orientation & numberElements flip(&header.image.pixelsPerLine, 2); // pixelsPerLine & linesPerImage for (int i = 0; i < header.image.numberElements; i++) { flip(&header.image.element[i].dataSign, 5); // dataSign, low/high stuff flip(&header.image.element[i].packing, 2); // packing & encoding flip(&header.image.element[i].dataOffset, 3); // dataOffset, eol/imagePadding } flip((U32*)(&header.film.frameRate), 1); flip((U32*)(&header.film.framePosition), 1); flip((U32*)(&header.film.sequenceLen), 1); flip((U32*)(&header.film.heldCount), 1); flip((U32*)(&header.film.shutterAngle), 1); flip((U32*)(&header.film.frameId), 1); flip((U32*)(&header.video.horzSampleRate), 10); flip(&header.video.timeCode, 2); flip((U32*)(&header.orientation.pixelAspect), 2); } else { flipped = false; } width = header.image.pixelsPerLine; height = header.image.linesPerImage; // Figure out the pixel aspect ratio. We recognize two possible // 'invalid' values -- all 0s or all 1s. // Equality is also invalid because Shake writes that for all images // Another bug version writes the image size as the pixel aspect, // ignore that. double pixel_aspect = 0; if ( header.orientation.pixelAspect[0] != 0 && header.orientation.pixelAspect[1] != 0 && header.orientation.pixelAspect[0] != 0xffffffff && header.orientation.pixelAspect[1] != 0xffffffff && header.orientation.pixelAspect[0] != header.orientation.pixelAspect[1] && (header.orientation.pixelAspect[0] != width || header.orientation.pixelAspect[1] != height)) { pixel_aspect = (double)header.orientation.pixelAspect[0] / (double)header.orientation.pixelAspect[1]; } // Set the image size. We will figure out the channels later from the elements, use rgb here: set_info(width, height, 3, pixel_aspect); #define DUMP_HEADER 0 #if DUMP_HEADER printf("%s:", header.file.imageFileName); if (flipped) printf(" flipped"); printf("\n"); // printf(" size=%dx%dx, ", header.image.pixelsPerLine, // header.image.linesPerImage); // printf(" orientation=%x\n", header.image.orientation); #endif int bitdepth = 0; for (int i = 0; i < header.image.numberElements; i++) { if (header.image.element[i].bits > bitdepth) bitdepth = header.image.element[i].bits; } ycbcr_hack = false; info_.channels(Mask_None); for (int i = 0; i < header.image.numberElements; i++) { #if DUMP_HEADER printf(" %d: ", i); // printf(" lowData=%d\n", header.image.element[i].lowData); // printf(" lowQuantity=%f\n", header.image.element[i].lowQuantity); // printf(" highData=%d\n", header.image.element[i].highData); // printf(" highQuantity=%f\n", header.image.element[i].highQuantity); printf("d %d, ", header.image.element[i].descriptor); printf("t %d, ", header.image.element[i].transfer); printf("c %d, ", header.image.element[i].colorimetric); printf("%d bits, ", header.image.element[i].bits); if (header.image.element[i].dataSign) printf("signed, "); if (header.image.element[i].packing) printf("filled=%d, ", header.image.element[i].packing); if (header.image.element[i].encoding) printf("rle=%d, ", header.image.element[i].encoding); // printf(" dataOffset=%d\n", header.image.element[i].dataOffset); if (header.image.element[i].eolPadding) printf("eolPadding=%d, ", header.image.element[i].eolPadding); // printf(" eoImagePadding=%d\n", header.image.element[i].eoImagePadding); printf("\"%s\"\n", header.image.element[i].description); #endif element[i].descriptor = header.image.element[i].descriptor; switch (element[i].descriptor) { case DESCRIPTOR_R: element[i].channels = Mask_Red; element[i].components = 1; break; case DESCRIPTOR_G: element[i].channels = Mask_Green; element[i].components = 1; break; case DESCRIPTOR_B: element[i].channels = Mask_Blue; element[i].components = 1; break; case DESCRIPTOR_A: element[i].channels = Mask_Alpha; element[i].components = 1; break; default: printf("Unknown DPX element descriptor %d\n", element[i].descriptor); case DESCRIPTOR_Y: element[i].channels = Mask_RGB; element[i].components = 1; break; case DESCRIPTOR_CbCr: element[i].channels = ChannelSetInit(6); // blue+green element[i].components = 1; if (i && element[0].descriptor == DESCRIPTOR_Y) { element[0].channels = Mask_Red; ycbcr_hack = true; } break; case DESCRIPTOR_Z: element[i].channels = Mask_Z; element[i].components = 1; break; case DESCRIPTOR_RGB: element[i].channels = Mask_RGB; element[i].components = 3; break; case DESCRIPTOR_RGBA: element[i].channels = Mask_RGBA; element[i].components = 4; break; case DESCRIPTOR_ABGR: element[i].channels = Mask_RGBA; element[i].components = 4; break; case DESCRIPTOR_CbYCrY: element[i].channels = Mask_RGB; element[i].components = 2; break; case DESCRIPTOR_CbYACrYA: element[i].channels = Mask_RGBA; element[i].components = 3; break; case DESCRIPTOR_CbYCr: element[i].channels = Mask_RGB; element[i].components = 3; break; case DESCRIPTOR_CbYCrA: element[i].channels = Mask_RGBA; element[i].components = 4; break; case DESCRIPTOR_USER_2: element[i].channels = ChannelSetInit(3); // red+green element[i].components = 2; break; case DESCRIPTOR_USER_3: element[i].channels = Mask_RGB; element[i].components = 3; break; case DESCRIPTOR_USER_4: element[i].channels = Mask_RGBA; element[i].components = 4; break; case DESCRIPTOR_USER_5: element[i].channels = Mask_RGBA; element[i].components = 5; break; case DESCRIPTOR_USER_6: element[i].channels = Mask_RGBA; element[i].components = 6; break; case DESCRIPTOR_USER_7: element[i].channels = Mask_RGBA; element[i].components = 7; break; case DESCRIPTOR_USER_8: element[i].channels = Mask_RGBA; element[i].components = 8; break; } element[i].bits = header.image.element[i].bits; element[i].packing = header.image.element[i].packing; //element[i].encoding = header.image.element[i].encoding; element[i].dataOffset = header.image.element[i].dataOffset; switch (element[i].bits) { case 1: element[i].bytes = (width * element[i].components + 31) / 32 * 4; break; case 8: element[i].bytes = (width * element[i].components + 3) & - 4; break; case 10: if (element[i].packing) { element[i].bytes = (width * element[i].components + 2) / 3 * 4; // detect stupid writers that did the math wrong struct stat s; fstat(fd, &s); if (element[i].dataOffset + element[i].bytes * height > size_t(s.st_size)) element[i].bytes = (width * element[i].components) / 3 * 4; } else element[i].bytes = (width * element[i].components * 10 + 31) / 32 * 4; break; case 12: if (element[i].packing) element[i].bytes = (width * element[i].components) * 2; else element[i].bytes = (width * element[i].components * 12 + 31) / 32 * 4; break; case 16: element[i].bytes = (width * element[i].components) * 2; break; case 32: // no sample files available for this case 64: // no sample files available for this default: printf("Unhandled DPX number of bits %d\n", element[i].bits); element[i].channels = Mask_None; break; } meta.setDataIfNotEmpty(MetaData::ELEMENT_DESCRIPTION[i], header.image.element[i].description); if (header.image.element[i].eolPadding != 0xffffffff) element[i].bytes += header.image.element[i].eolPadding; info_.turn_on(element[i].channels); } unsigned v = header.video.timeCode; char buffer[12]; sprintf(buffer, "%02x:%02x:%02x:%02x", v >> 24, (v >> 16) & 255, (v >> 8) & 255, v & 255); iop->setTimeCode(buffer); std::string timecode = buffer; std::string edgecode; DPXFilmHeader* s = &(header.film); if ( s->filmManufacturingIdCode[0] && s->filmType[0] && s->perfsOffset[0] && s->prefix[0] && s->count[0] ) { char buffer[22]; sprintf( buffer, "%c%c %c%c %c%c%c%c%c%c %c%c%c%c %c%c", s->filmManufacturingIdCode[0], s->filmManufacturingIdCode[1], s->filmType[0], s->filmType[1], s->prefix[0], s->prefix[1], s->prefix[2], s->prefix[3], s->prefix[4], s->prefix[5], s->count[0], s->count[1], s->count[2], s->count[3], s->perfsOffset[0], s->perfsOffset[1] ); edgecode = buffer; sprintf( buffer, "%c%c %c%c %c%c %c%c%c%c %c%c%c%c %c%c", s->filmManufacturingIdCode[0], s->filmManufacturingIdCode[1], s->filmType[0], s->filmType[1], s->prefix[0], s->prefix[1], s->prefix[2], s->prefix[3], s->prefix[4], s->prefix[5], s->count[0], s->count[1], s->count[2], s->count[3], s->perfsOffset[0], s->perfsOffset[1] ); iop->setEdgeCode(SanitizeString(buffer)); } orientation = header.image.orientation; info_.ydirection((orientation & 2) ? 1 : -1); switch (header.image.element[0].transfer) { case TRANSFER_USER: // seems to be used by some log files case TRANSFER_DENSITY: case TRANSFER_LOG: lut_ = LUT::getLut(LUT::LOG); meta.setData(MetaData::DPX::TRANSFER, "log"); break; case TRANSFER_CCIR_709_1: lut_ = LUT::builtin("rec709"); meta.setData(MetaData::DPX::TRANSFER, "rec709"); break; case TRANSFER_LINEAR: // unfortunatly too much software writes this for sRGB... default: lut_ = LUT::getLut(header.image.element[0].bits <= 8 ? LUT::INT8 : LUT::INT16); meta.setData(MetaData::DPX::TRANSFER, "sRGB"); break; } // XXXX meta.setDataIfNotEmpty(MetaData::COPYRIGHT, header.file.copyright); meta.setDataIfNotEmpty(MetaData::DPX::FILE_NAME, header.orientation.fileName); meta.setDataIfNotEmpty(MetaData::DPX::CREATION_TIME, header.orientation.creationTime); meta.setDataIfNotEmpty(MetaData::DPX::INPUT_DEVICE, header.orientation.inputName); meta.setDataIfNotEmpty(MetaData::DPX::INPUT_SN, header.orientation.inputSN); meta.setData(MetaData::TIMECODE, timecode); if (pixel_aspect != 0) { meta.setData(MetaData::PIXEL_ASPECT, pixel_aspect); } meta.setData(MetaData::DEPTH, MetaData::DEPTH_FIXED(bitdepth)); // Television if ((header.video.timeCode != 0) && (isfinite(header.video.timeCode))) { meta.setData(MetaData::DPX::TIME_CODE, header.video.timeCode); } if ((header.video.userBits != 0) && (isfinite(header.video.userBits))) { meta.setData(MetaData::DPX::USER_BITS, header.video.userBits); } if ((header.video.interlace != 0) && (isfinite(header.video.interlace))) { meta.setData(MetaData::DPX::INTERLACE, header.video.interlace); } if ((header.video.fieldNumber != 0) && (isfinite(header.video.fieldNumber))) { meta.setData(MetaData::DPX::FIELD_NUMBER, header.video.fieldNumber); } if ((header.video.videoSignal != 0) && (isfinite(header.video.videoSignal))) { meta.setData(MetaData::DPX::VIDEO_SIGNAL, header.video.videoSignal); } if ((header.video.horzSampleRate != 0) && (isfinite(header.video.horzSampleRate))) { meta.setData(MetaData::DPX::HORIZ_SAMPLE, header.video.horzSampleRate); } if ((header.video.vertSampleRate != 0) && (isfinite(header.video.vertSampleRate))) { meta.setData(MetaData::DPX::VERT_SAMPLE, header.video.vertSampleRate); } if ((header.film.frameRate != 0) && (isfinite(header.film.frameRate))) { meta.setData(MetaData::FRAME_RATE, header.film.frameRate); } if ((header.video.frameRate != 0) && (isfinite(header.video.frameRate))) { meta.setData(MetaData::DPX::FRAME_RATE, header.video.frameRate); } if ((header.video.timeOffset != 0) && (isfinite(header.video.timeOffset))) { meta.setData(MetaData::DPX::TIME_OFFSET, header.video.timeOffset); } if ((header.video.gamma != 0) && (isfinite(header.video.gamma))) { meta.setData(MetaData::DPX::GAMMA, header.video.gamma); } if ((header.video.blackLevel != 0) && (isfinite(header.video.blackLevel))) { meta.setData(MetaData::DPX::BLACK_LEVEL, header.video.blackLevel); } if ((header.video.blackGain != 0) && (isfinite(header.video.blackGain))) { meta.setData(MetaData::DPX::BLACK_GAIN, header.video.blackGain); } if ((header.video.breakpoint != 0) && (isfinite(header.video.breakpoint))) { meta.setData(MetaData::DPX::BREAK_POINT, header.video.breakpoint); } if ((header.video.whiteLevel != 0) && (isfinite(header.video.whiteLevel))) { meta.setData(MetaData::DPX::WHITE_LEVEL, header.video.whiteLevel); } if ((header.video.integrationTimes != 0) && (isfinite(header.video.integrationTimes))) { meta.setData(MetaData::DPX::INTEGRATION_TIMES, header.video.integrationTimes); } // if (header.film.framePosition != UNDEF_R32) { meta.setData(MetaData::DPX::FRAMEPOS, header.film.framePosition); // } if (header.film.sequenceLen != UNDEF_U32) { meta.setData(MetaData::DPX::SEQUENCE_LENGTH, header.film.sequenceLen); } if (header.film.heldCount != UNDEF_U32) { meta.setData(MetaData::DPX::HELD_COUNT, header.film.heldCount); } if (header.film.shutterAngle != UNDEF_R32) { meta.setData(MetaData::SHUTTER_ANGLE, header.film.shutterAngle); } meta.setDataIfNotEmpty(MetaData::DPX::FRAME_ID, header.film.frameId); meta.setDataIfNotEmpty(MetaData::SLATE_INFO, header.film.slateInfo); if (edgecode.length() && edgecode != "00 00 000000 0000 00") { meta.setData(MetaData::EDGECODE, edgecode); } meta.setTimeStamp(MetaData::FILE_CREATION_TIME, header.file.creationTime); meta.setDataIfNotEmpty(MetaData::CREATOR, header.file.creator); meta.setDataIfNotEmpty(MetaData::PROJECT, header.file.project); meta.setDataIfNotEmpty(MetaData::COPYRIGHT, header.file.copyright); meta.setDataIfNotEmpty(MetaData::FILENAME, header.file.imageFileName); } ~dpxReader() {} void CConvert(float* dest, const uchar* src, int x, int r, int delta) { const float m = 1.0f / 255; const float off = .5f - 0x80 * m; for (; x < r; x++) dest[x] = src[x * delta] * m + off; } void CConvert(float* dest, const U16* src, int x, int r, int delta, int bits) { const float m = 1.0f / ((1 << bits) - 1); const float off = .5f - (1 << (bits - 1)) * m; for (; x < r; x++) dest[x] = src[x * delta] * m + off; } void CbConvert(float* dest, const uchar* src, int x, int r, int delta) { const float m = 1.0f / 255; const float m2 = m / 2; const float off = .5f - 0x80 * m; if (!(r & 1) && r >= int(width)) { dest[r - 1] = src[(r - 2) * delta] * m + off; r--; } for (; x < r; x++) { if (x & 1) dest[x] = (src[(x - 1) * delta] + src[(x + 1) * delta]) * m2 + off; else dest[x] = src[x * delta] * m + off; } } void CbConvert(float* dest, const U16* src, int x, int r, int delta, int bits) { const float m = 1.0f / ((1 << bits) - 1); const float m2 = m / 2; const float off = .5f - (1 << (bits - 1)) * m; if (!(r & 1) && r >= int(width)) { dest[r - 1] = src[(r - 2) * delta] * m + off; r--; } for (; x < r; x++) { if (x & 1) dest[x] = (src[(x - 1) * delta] + src[(x + 1) * delta]) * m2 + off; else dest[x] = src[x * delta] * m + off; } } void CrConvert(float* dest, const uchar* src, int x, int r, int delta) { const float m = 1.0f / 255; const float m2 = m / 2; const float off = .5f - 0x80 * m; if (!x) { dest[x] = src[(x + 1) * delta] * m + off; x++; } for (; x < r; x++) { if (x & 1) dest[x] = src[x * delta] * m + off; else dest[x] = (src[(x - 1) * delta] + src[(x + 1) * delta]) * m2 + off; } } void CrConvert(float* dest, const U16* src, int x, int r, int delta, int bits) { const float m = 1.0f / ((1 << bits) - 1); const float m2 = m / 2; const float off = .5f - (1 << (bits - 1)) * m; if (!x) { dest[x] = src[(x + 1) * delta] * m + off; x++; } for (; x < r; x++) { if (x & 1) dest[x] = src[x * delta] * m + off; else dest[x] = (src[(x - 1) * delta] + src[(x + 1) * delta]) * m2 + off; } } void YConvert(float* dest, const uchar* src, int x, int r, int delta) { Linear::from_byte(dest + x, src + x * delta, r - x, delta); } void YConvert(float* dest, const U16* src, int x, int r, int delta, int bits) { Linear::from_short(dest + x, src + x * delta, r - x, bits, delta); } void AConvert(float* dest, const uchar* src, int x, int r, int delta) { Linear::from_byte(dest + x, src + x * delta, r - x, delta); } void AConvert(float* dest, const U16* src, int x, int r, int delta, int bits) { Linear::from_short(dest + x, src + x * delta, r - x, bits, delta); } void fixYCbCr(int x, int r, bool alpha, Row& row) { if (iop->raw()) return; float* R = row.writable(Chan_Red); float* G = row.writable(Chan_Green); float* B = row.writable(Chan_Blue); for (int X = x; X < r; X++) { float y = (R[X] - float(16 / 255.0f)) * float(255.0f / 219); float u = (G[X] - .5f) * float(255.0f / 224); float v = (B[X] - .5f) * float(255.0f / 224); R[X] = v * (2 - 2 * Kr) + y; G[X] = y - v * ((2 - 2 * Kr) * Kr / (1 - Kr - Kb)) - u * ((2 - 2 * Kb) * Kb / (1 - Kr - Kb)); B[X] = u * (2 - 2 * Kb) + y; } from_float(Chan_Red, R + x, R + x, alpha ? row[Chan_Alpha] + x : 0, r - x); from_float(Chan_Green, G + x, G + x, alpha ? row[Chan_Alpha] + x : 0, r - x); from_float(Chan_Blue, B + x, B + x, alpha ? row[Chan_Alpha] + x : 0, r - x); } void read_element8(const Element& e, int y, int x, int r, Row& row) { // uncompress into an array of bytes: ARRAY(uchar, buf, width * e.components); if (e.bits == 1) { unsigned n = (e.bytes + 3) / 4; ARRAY(U32, src, n); read(src, e.dataOffset + y * e.bytes, e.bytes); if (flipped) flip(src, n); for (unsigned x = 0; x < width * e.components; x++) buf[x] = (src[x / 32] & (1 << (x & 31))) ? 255 : 0; } else { read(buf, e.dataOffset + y * e.bytes, width * e.components); } // now convert to rgb switch (e.descriptor) { case DESCRIPTOR_CbCr: // to actually get rgb we need the Y from the other element. NYI CbConvert(row.writable(Chan_Green), buf, x, r, 1); CrConvert(row.writable(Chan_Blue), buf, x, r, 1); if (ycbcr_hack) fixYCbCr(x, r, false, row); break; case DESCRIPTOR_RGBA: { const uchar* alpha = buf + x * 4 + 3; foreach(z, e.channels) from_byte(z, row.writable(z) + x, buf + x * 4 + (z - 1), alpha, r - x, 4); break; } case DESCRIPTOR_ABGR: { const uchar* alpha = buf + x * 4; foreach(z, e.channels) from_byte(z, row.writable(z) + x, buf + x * 4 + (4 - z), alpha, r - x, 4); break; } case DESCRIPTOR_CbYCrY: YConvert(row.writable(Chan_Red), buf + 1, x, r, 2); CbConvert(row.writable(Chan_Green), buf, x, r, 2); CrConvert(row.writable(Chan_Blue), buf, x, r, 2); fixYCbCr(x, r, false, row); break; case DESCRIPTOR_CbYACrYA: CbConvert(row.writable(Chan_Green), buf, x, r, 3); CrConvert(row.writable(Chan_Blue), buf, x, r, 3); YConvert(row.writable(Chan_Red), buf + 1, x, r, 3); AConvert(row.writable(Chan_Alpha), buf + 2, x, r, 3); fixYCbCr(x, r, true, row); break; case DESCRIPTOR_CbYCr: CConvert(row.writable(Chan_Green), buf, x, r, 3); YConvert(row.writable(Chan_Red), buf + 1, x, r, 3); CConvert(row.writable(Chan_Blue), buf + 2, x, r, 3); fixYCbCr(x, r, false, row); break; case DESCRIPTOR_CbYCrA: CConvert(row.writable(Chan_Green), buf, x, r, 4); YConvert(row.writable(Chan_Red), buf + 1, x, r, 4); CConvert(row.writable(Chan_Blue), buf + 2, x, r, 4); AConvert(row.writable(Chan_Alpha), buf + 3, x, r, 4); fixYCbCr(x, r, true, row); break; case DESCRIPTOR_Y: if (ycbcr_hack) { YConvert(row.writable(Chan_Red), buf, x, r, 1); break; } // else fall through: default: { int Z = 0; foreach(z, e.channels) { from_byte(z, row.writable(z) + x, buf + Z + x * e.components, 0 /*alpha*/, r - x, e.components); if (Z + 1 < e.components) Z++; } break; } } } void read_element16(const Element& e, int y, int x, int r, Row& row) { ARRAY(U16, buf, width * e.components + 2); switch (e.bits) { case 10: { unsigned n = (e.bytes + 3) / 4; unsigned x; ARRAY(U32, src, n); read(src, e.dataOffset + y * e.bytes, e.bytes); if (flipped) flip(src, n); switch (e.packing) { case 0: for (x = 0; x < width * e.components; x++) { unsigned a = (x * 10) / 32; unsigned b = (x * 10) % 32; if (b > 22) buf[x] = ((src[a + 1] << (32 - b)) + (src[a] >> b)) & 0x3ff; else buf[x] = (src[a] >> b) & 0x3ff; } break; case 1: for (x = 0; x < n; x++) { buf[3 * x + 0] = (src[x] >> 22) & 0x3ff; buf[3 * x + 1] = (src[x] >> 12) & 0x3ff; buf[3 * x + 2] = (src[x] >> 02) & 0x3ff; } break; case 2: for (x = 0; x < n; x++) { buf[3 * x + 0] = (src[x] >> 20) & 0x3ff; buf[3 * x + 1] = (src[x] >> 10) & 0x3ff; buf[3 * x + 2] = (src[x] >> 00) & 0x3ff; } break; } break; } case 12: switch (e.packing) { case 0: { unsigned n = (e.bytes + 3) / 4; ARRAY(U32, src, n); read(src, e.dataOffset + y * e.bytes, e.bytes); if (flipped) flip(src, n); for (unsigned x = 0; x < width * e.components; x++) { unsigned a = (x * 12) / 32; unsigned b = (x * 12) % 32; if (b > 20) buf[x] = ((src[a + 1] << (32 - b)) + (src[a] >> b)) & 0xfff; else buf[x] = (src[a] >> b) & 0xfff; } break; } case 1: { unsigned n = width * e.components; read(buf, e.dataOffset + y * e.bytes, n * 2); if (flipped) flip(buf, n); for (unsigned x = 0; x < n; x++) buf[x] >>= 4; break; } case 2: { unsigned n = width * e.components; read(buf, e.dataOffset + y * e.bytes, n * 2); if (flipped) flip(buf, n); for (unsigned x = 0; x < n; x++) buf[x] &= 0xfff; break; } } break; case 16: read(buf, e.dataOffset + y * e.bytes, width * e.components * 2); if (flipped) flip(buf, width * e.components); break; } // now convert to rgb switch (e.descriptor) { case DESCRIPTOR_CbCr: // to actually get rgb we need the Y from the other element. NYI CbConvert(row.writable(Chan_Green), buf, x, r, 1, e.bits); CrConvert(row.writable(Chan_Blue), buf, x, r, 1, e.bits); if (ycbcr_hack) fixYCbCr(x, r, false, row); break; case DESCRIPTOR_RGBA: { const U16* alpha = buf + x * 4 + 3; foreach(z, e.channels) from_short(z, row.writable(z) + x, buf + x * 4 + (z - 1), alpha, r - x, e.bits, 4); break; } case DESCRIPTOR_ABGR: { const U16* alpha = buf + x * 4; foreach(z, e.channels) from_short(z, row.writable(z) + x, buf + x * 4 + (4 - z), alpha, r - x, e.bits, 4); break; } case DESCRIPTOR_CbYCrY: YConvert(row.writable(Chan_Red), buf + 1, x, r, 2, e.bits); CbConvert(row.writable(Chan_Green), buf, x, r, 2, e.bits); CrConvert(row.writable(Chan_Blue), buf, x, r, 2, e.bits); fixYCbCr(x, r, false, row); break; case DESCRIPTOR_CbYACrYA: CbConvert(row.writable(Chan_Green), buf, x, r, 3, e.bits); CrConvert(row.writable(Chan_Blue), buf, x, r, 3, e.bits); YConvert(row.writable(Chan_Red), buf + 1, x, r, 3, e.bits); AConvert(row.writable(Chan_Alpha), buf + 2, x, r, 3, e.bits); fixYCbCr(x, r, true, row); break; case DESCRIPTOR_CbYCr: YConvert(row.writable(Chan_Red), buf + 1, x, r, 3, e.bits); if (e.bits == 10) { // The "flowers" 10-bit sample image has the Cr/Cb reversed. This may be // a mistake, and other small test images have them the other way. // However I am leaving this reversed as that is how previous versions // of Nuke read this file. CConvert(row.writable(Chan_Blue), buf, x, r, 3, e.bits); CConvert(row.writable(Chan_Green), buf + 2, x, r, 3, e.bits); } else { CConvert(row.writable(Chan_Green), buf, x, r, 3, e.bits); CConvert(row.writable(Chan_Blue), buf + 2, x, r, 3, e.bits); } fixYCbCr(x, r, false, row); break; case DESCRIPTOR_CbYCrA: CConvert(row.writable(Chan_Green), buf, x, r, 4, e.bits); YConvert(row.writable(Chan_Red), buf + 1, x, r, 4, e.bits); CConvert(row.writable(Chan_Blue), buf + 2, x, r, 4, e.bits); AConvert(row.writable(Chan_Alpha), buf + 3, x, r, 4, e.bits); fixYCbCr(x, r, true, row); break; case DESCRIPTOR_Y: if (ycbcr_hack) { YConvert(row.writable(Chan_Red), buf, x, r, 1, e.bits); break; } // else fall through: default: { int Z = 0; foreach(z, e.channels) { from_short(z, row.writable(z) + x, buf + Z + x * e.components, 0 /*alpha*/, r - x, e.bits, e.components); if (Z + 1 < e.components) Z++; } break; } } } void read_element(const Element& e, int y, int x, int r, Row& row) { if (e.bits <= 8) read_element8(e, y, x, r, row); else read_element16(e, y, x, r, row); } void engine(int y, int x, int r, ChannelMask channels, Row& row) { if (!(orientation & 2)) y = height - y - 1; ChannelSet remaining(channels); if (ycbcr_hack && (channels & Mask_RGB)) remaining += Mask_RGB; for (unsigned i = 0; i < 8; i++) { if (element[i].channels & remaining) { read_element(element[i], y, x, r, row); remaining -= element[i].channels; if (!remaining) break; } } } }; static bool test(int fd, const unsigned char* block, int length) { DPXHeader* header = (DPXHeader*)block; U32 m = header->file.magicNumber; if (m == DPX_MAGIC || m == DPX_MAGIC_FLIPPED) return true; return false; } static Reader* build(Read* iop, int fd, const unsigned char* b, int n) { return new dpxReader(iop, fd, b, n); } const Reader::Description dpxReader::description("dpx\0", build, test); // end dpxReader.C