public void parseData() { if (stream.Length < 16) { throw new TiffParserException("Not a TIFF file (size too small)"); } Endianness endian = Endianness.little; byte[] data = new byte[5]; stream.Position = 0; stream.Read(data, 0, 4); if (data[0] == 0x4D || data[1] == 0x4D) { //open binaryreader reader = new TIFFBinaryReaderRE(stream); endian = Endianness.big; if (data[3] != 42 && data[2] != 0x4f) // ORF sometimes has 0x4f, Lovely! { throw new TiffParserException("Not a TIFF file (magic 42)"); } } else if (data[0] == 0x49 || data[1] == 0x49) { reader = new TIFFBinaryReader(stream); if (data[2] != 42 && data[2] != 0x52 && data[2] != 0x55) // ORF has 0x52, RW2 0x55 - Brillant! { throw new TiffParserException("Not a TIFF file (magic 42)"); } } else { throw new TiffParserException("Not a TIFF file (ID)"); } UInt32 nextIFD; reader.Position = 4; nextIFD = reader.ReadUInt32(); rootIFD = new IFD(reader, nextIFD, endian, 0); nextIFD = rootIFD.nextOffset; while (nextIFD != 0) { rootIFD.subIFD.Add(new IFD(reader, nextIFD, endian, 0)); if (rootIFD.subIFD.Count > 100) { throw new TiffParserException("TIFF file has too many SubIFDs, probably broken"); } nextIFD = (rootIFD.subIFD[rootIFD.subIFD.Count - 1]).nextOffset; } }
void setBlack(IFD raw) { if (raw.hasEntry(TagType.MASKEDAREAS)) { if (decodeMaskedAreas(raw)) { return; } } if (raw.getEntry(TagType.BLACKLEVEL) != null) { decodeBlackLevels(raw); } }
public void mergeIFD(TiffParser other_tiff) { if (other_tiff?.rootIFD?.subIFD.Count == 0) { return; } IFD other_root = other_tiff.rootIFD; foreach (IFD i in other_root.subIFD) { rootIFD.subIFD.Add(i); } foreach (KeyValuePair <TagType, Tag> i in other_root.tags) { rootIFD.tags.Add(i.Key, i.Value);; } other_root.subIFD.Clear(); other_root.subIFD.Clear(); }
public DngDecoder(IFD rootIFD, ref TIFFBinaryReader file) : base(ref file) { mRootIFD = (rootIFD); List <IFD> data = mRootIFD.getIFDsWithTag(TagType.DNGVERSION); var v = data[0].getEntry(TagType.DNGVERSION).data; if ((byte)v[0] != 1) { throw new RawDecoderException("Not a supported DNG image format: " + (int)v[0] + (int)v[1] + (int)v[2] + (int)v[3]); } // if (v[1] > 4) // throw new RawDecoderException("Not a supported DNG image format: v%u.%u.%u.%u", (int)v[0], (int)v[1], (int)v[2], (int)v[3]); if (((byte)v[0] <= 1) && ((byte)v[1] < 1)) // Prior to v1.1.xxx fix LJPEG encoding bug { mFixLjpeg = true; } else { mFixLjpeg = false; } }
/* Decodes DNG masked areas into blackareas in the image */ bool decodeMaskedAreas(IFD raw) { Tag masked = raw.getEntry(TagType.MASKEDAREAS); if (masked.dataType != TiffDataType.SHORT && masked.dataType != TiffDataType.LONG) { return(false); } Int32 nrects = (int)masked.dataCount / 4; if (0 == nrects) { return(false); } /* Since we may both have short or int, copy it to int array. */ masked.getIntArray(out Int32[] rects, nrects * 4); iPoint2D top = mRaw.mOffset; for (UInt32 i = 0; i < nrects; i++) { iPoint2D topleft = new iPoint2D(rects[i * 4 + 1], rects[i * 4]); iPoint2D bottomright = new iPoint2D(rects[i * 4 + 3], rects[i * 4 + 2]); // Is this a horizontal box, only add it if it covers the active width of the image if (topleft.x <= top.x && bottomright.x >= (mRaw.dim.x + top.x)) { mRaw.blackAreas.Add(new BlackArea(topleft.y, bottomright.y - topleft.y, false)); } // Is it a vertical box, only add it if it covers the active height of the image else if (topleft.y <= top.y && bottomright.y >= (mRaw.dim.y + top.y)) { mRaw.blackAreas.Add(new BlackArea(topleft.x, bottomright.x - topleft.x, true)); } } return(mRaw.blackAreas.Count != 0); }
public TiffDecoder(IFD rootifd, ref TIFFBinaryReader file) : base(ref file) { decoderVersion = 1; ifd = rootifd; //check if no }
protected override Thumbnail decodeThumbInternal() { //find the preview IFD (usually the first if any) try { List <IFD> potential = mRootIFD.getIFDsWithTag(TagType.NEWSUBFILETYPE); if (potential != null || potential.Count != 0) { IFD thumbIFD = null; for (int i = 0; i < potential.Count; i++) { var subFile = potential[i].getEntry(TagType.NEWSUBFILETYPE); if (subFile.getInt() == 1) { thumbIFD = potential[i]; break; } } if (thumbIFD != null) { //there is a thumbnail UInt32 sample_format = 1; UInt32 bps = thumbIFD.getEntry(TagType.BITSPERSAMPLE).getUInt(); iPoint2D dim; if (thumbIFD.hasEntry(TagType.SAMPLEFORMAT)) { sample_format = thumbIFD.getEntry(TagType.SAMPLEFORMAT).getUInt(); } try { dim = new iPoint2D() { x = thumbIFD.getEntry(TagType.IMAGEWIDTH).getInt(), y = thumbIFD.getEntry(TagType.IMAGELENGTH).getInt() }; } catch (TiffParserException) { throw new RawDecoderException("DNG Decoder: Could not read basic image information."); } int compression = thumbIFD.getEntry(TagType.COMPRESSION).getShort(); // Now load the image if (compression == 1) { // Uncompressed. UInt32 cpp = thumbIFD.getEntry(TagType.SAMPLESPERPIXEL).getUInt(); if (cpp > 4) { throw new RawDecoderException("DNG Decoder: More than 4 samples per pixel is not supported."); } Tag offsets = thumbIFD.getEntry(TagType.STRIPOFFSETS); Tag counts = thumbIFD.getEntry(TagType.STRIPBYTECOUNTS); UInt32 yPerSlice = thumbIFD.getEntry(TagType.ROWSPERSTRIP).getUInt(); UInt32 width = thumbIFD.getEntry(TagType.IMAGEWIDTH).getUInt(); UInt32 height = thumbIFD.getEntry(TagType.IMAGELENGTH).getUInt(); if (counts.dataCount != offsets.dataCount) { throw new RawDecoderException("DNG Decoder: Byte count number does not match strip size: count:" + counts.dataCount + ", strips:" + offsets.dataCount); } UInt32 offY = 0; List <DngStrip> slices = new List <DngStrip>(); for (UInt32 s = 0; s < offsets.dataCount; s++) { DngStrip slice = new DngStrip(); slice.offset = offsets.getUInt(s); slice.count = counts.getUInt(s); slice.offsetY = offY; if (offY + yPerSlice > height) { slice.h = height - offY; } else { slice.h = yPerSlice; } offY += yPerSlice; if (mFile.isValid(slice.offset, slice.count)) // Only decode if size is valid { slices.Add(slice); } } for (int i = 0; i < slices.Count; i++) { DngStrip slice = slices[i]; TIFFBinaryReader input = new TIFFBinaryReader(mFile.BaseStream, slice.offset, (uint)mFile.BaseStream.Length); iPoint2D size = new iPoint2D((int)width, (int)slice.h); iPoint2D pos = new iPoint2D(0, (int)slice.offsetY); bool big_endian = (thumbIFD.endian == Endianness.big); // DNG spec says that if not 8 or 16 bit/sample, always use big endian if (bps != 8 && bps != 16) { big_endian = true; } try { // readUncompressedRaw(ref input, size, pos, (int)(mRaw.cpp * width * bps / 8), (int)bps, big_endian ? BitOrder.Jpeg : BitOrder.Plain); } catch (IOException ex) { throw new RawDecoderException("DNG decoder: IO error occurred in first slice, unable to decode more. Error is: " + ex.Message); } } } else if (compression == 7 || compression == 0x884c) { /* * // Let's try loading it as tiles instead * * uint cpp = (thumbIFD.getEntry(TagType.SAMPLESPERPIXEL).getUInt()); * * if (sample_format != 1) * throw new RawDecoderException("DNG Decoder: Only 16 bit unsigned data supported for compressed data."); * * DngDecoderSlices slices = new DngDecoderSlices(mFile, mRaw, compression); * if (thumbIFD.hasEntry(TagType.TILEOFFSETS)) * { * UInt32 tilew = thumbIFD.getEntry(TagType.TILEWIDTH).getUInt(); * UInt32 tileh = thumbIFD.getEntry(TagType.TILELENGTH).getUInt(); * if (tilew == 0 || tileh == 0) * throw new RawDecoderException("DNG Decoder: Invalid tile size"); * * UInt32 tilesX = (uint)(mRaw.dim.x + tilew - 1) / tilew; * UInt32 tilesY = (uint)(mRaw.dim.y + tileh - 1) / tileh; * UInt32 nTiles = tilesX * tilesY; * * Tag offsets = thumbIFD.getEntry(TagType.TILEOFFSETS); * Tag counts = thumbIFD.getEntry(TagType.TILEBYTECOUNTS); * if (offsets.dataCount != counts.dataCount || offsets.dataCount != nTiles) * throw new RawDecoderException("DNG Decoder: Tile count mismatch: offsets:" + offsets.dataCount + " count:" + counts.dataCount + ", calculated:" + nTiles); * * slices.mFixLjpeg = mFixLjpeg; * * for (UInt32 y = 0; y < tilesY; y++) * { * for (UInt32 x = 0; x < tilesX; x++) * { * DngSliceElement e = new DngSliceElement(offsets.getUInt(x + y * tilesX), counts.getUInt(x + y * tilesX), tilew * x, tileh * y); * e.mUseBigtable = tilew * tileh > 1024 * 1024; * slices.addSlice(e); * } * } * } * else * { // Strips * Tag offsets = thumbIFD.getEntry(TagType.STRIPOFFSETS); * Tag counts = thumbIFD.getEntry(TagType.STRIPBYTECOUNTS); * * UInt32 yPerSlice = thumbIFD.getEntry(TagType.ROWSPERSTRIP).getUInt(); * * if (counts.dataCount != offsets.dataCount) * { * throw new RawDecoderException("DNG Decoder: Byte count number does not match strip size: count:" + counts.dataCount + ", stips:" + offsets.dataCount); * } * * if (yPerSlice == 0 || yPerSlice > (UInt32)dim.y) * throw new RawDecoderException("DNG Decoder: Invalid y per slice"); * * UInt32 offY = 0; * for (UInt32 s = 0; s < counts.dataCount; s++) * { * DngSliceElement e = new DngSliceElement(offsets.getUInt(s), counts.getUInt(s), 0, offY); * e.mUseBigtable = yPerSlice * mRaw.dim.y > 1024 * 1024; * offY += yPerSlice; * * if (mFile.isValid(e.byteOffset, e.byteCount)) // Only decode if size is valid * slices.addSlice(e); * } * } * UInt32 nSlices = (uint)slices.slices.Count; * if (nSlices == 0) * throw new RawDecoderException("DNG Decoder: No valid slices found."); * * slices.decodeSlice(); */ } } } } catch (Exception) { //thumbnail are optional so ignore all exception } return(null); }
protected override RawImage decodeRawInternal() { List <IFD> data = mRootIFD.getIFDsWithTag(TagType.COMPRESSION); if (data.Count == 0) { throw new RawDecoderException("DNG Decoder: No image data found"); } // Erase the ones not with JPEG compression for (int k = data.Count - 1; k >= 0; k--) { IFD i = data[k]; int comp = i.getEntry(TagType.COMPRESSION).getShort(0); bool isSubsampled = false; try { isSubsampled = (i.getEntry(TagType.NEWSUBFILETYPE).getInt() & 1) != 0; // bit 0 is on if image is subsampled } catch (TiffParserException) { } if ((comp != 7 && comp != 1 && comp != 0x884c) || isSubsampled) { // Erase if subsampled, or not JPEG or uncompressed data.Remove(i); } } if (data.Count == 0) { throw new RawDecoderException("DNG Decoder: No RAW chunks found"); } /* * if (data.size() > 1) * { * _RPT0(0, "Multiple RAW chunks found - using first only!"); * }*/ IFD raw = data[0]; UInt32 sample_format = 1; UInt32 bps = raw.getEntry(TagType.BITSPERSAMPLE).getUInt(); if (raw.hasEntry(TagType.SAMPLEFORMAT)) { sample_format = raw.getEntry(TagType.SAMPLEFORMAT).getUInt(); } if (sample_format != 1) { throw new RawDecoderException("DNG Decoder: Only 16 bit unsigned data supported."); } mRaw.isCFA = (raw.getEntry(TagType.PHOTOMETRICINTERPRETATION).getUShort() == 32803); /* * if (mRaw.isCFA) * _RPT0(0, "This is a CFA image\n"); * else * _RPT0(0, "This is NOT a CFA image\n"); */ if (sample_format == 1 && bps > 16) { throw new RawDecoderException("DNG Decoder: Integer precision larger than 16 bits currently not supported."); } if (sample_format == 3 && bps != 32) { throw new RawDecoderException("DNG Decoder: Float point must be 32 bits per sample."); } try { mRaw.dim = new iPoint2D(); mRaw.dim.x = raw.getEntry(TagType.IMAGEWIDTH).getInt(); mRaw.dim.y = raw.getEntry(TagType.IMAGELENGTH).getInt(); } catch (TiffParserException) { throw new RawDecoderException("DNG Decoder: Could not read basic image information."); } //init the raw image mRaw.Init(); mRaw.colorDepth = (ushort)bps; int compression = -1; try { compression = raw.getEntry(TagType.COMPRESSION).getShort(); if (mRaw.isCFA) { // Check if layout is OK, if present if (raw.hasEntry(TagType.CFALAYOUT)) { if (raw.getEntry(TagType.CFALAYOUT).getShort() != 1) { throw new RawDecoderException("DNG Decoder: Unsupported CFA Layout."); } } Tag cfadim = raw.getEntry(TagType.CFAREPEATPATTERNDIM); if (cfadim.dataCount != 2) { throw new RawDecoderException("DNG Decoder: Couldn't read CFA pattern dimension"); } Tag pDim = raw.getEntry(TagType.CFAREPEATPATTERNDIM); // Get the size var cPat = raw.getEntry(TagType.CFAPATTERN).data; // Does NOT contain dimensions as some documents state /* * if (raw.hasEntry(CFAPLANECOLOR)) { * Tag e = raw.getEntry(CFAPLANECOLOR); * unsigned stringcPlaneOrder = e.getData(); // Map from the order in the image, to the position in the CFA * printf("Planecolor: "); * for (UInt32 i = 0; i < e.count; i++) { * printf("%u,",cPlaneOrder[i]); * } * printf("\n"); * } */ iPoint2D cfaSize = new iPoint2D(pDim.getInt(1), pDim.getInt(0)); mRaw.cfa.setSize(cfaSize); if (cfaSize.area() != raw.getEntry(TagType.CFAPATTERN).dataCount) { throw new RawDecoderException("DNG Decoder: CFA pattern dimension and pattern count does not match: " + raw.getEntry(TagType.CFAPATTERN).dataCount); } for (int y = 0; y < cfaSize.y; y++) { for (int x = 0; x < cfaSize.x; x++) { UInt32 c1 = Convert.ToUInt32(cPat[x + y * cfaSize.x]); CFAColor c2; switch (c1) { case 0: c2 = CFAColor.RED; break; case 1: c2 = CFAColor.GREEN; break; case 2: c2 = CFAColor.BLUE; break; case 3: c2 = CFAColor.CYAN; break; case 4: c2 = CFAColor.MAGENTA; break; case 5: c2 = CFAColor.YELLOW; break; case 6: c2 = CFAColor.WHITE; break; default: c2 = CFAColor.UNKNOWN; throw new RawDecoderException("DNG Decoder: Unsupported CFA Color."); } mRaw.cfa.setColorAt(new iPoint2D(x, y), c2); } } } // Now load the image if (compression == 1) { // Uncompressed. try { UInt32 cpp = raw.getEntry(TagType.SAMPLESPERPIXEL).getUInt(); if (cpp > 4) { throw new RawDecoderException("DNG Decoder: More than 4 samples per pixel is not supported."); } mRaw.cpp = cpp; Tag offsets = raw.getEntry(TagType.STRIPOFFSETS); Tag counts = raw.getEntry(TagType.STRIPBYTECOUNTS); UInt32 yPerSlice = raw.getEntry(TagType.ROWSPERSTRIP).getUInt(); UInt32 width = raw.getEntry(TagType.IMAGEWIDTH).getUInt(); UInt32 height = raw.getEntry(TagType.IMAGELENGTH).getUInt(); if (counts.dataCount != offsets.dataCount) { throw new RawDecoderException("DNG Decoder: Byte count number does not match strip size: count:" + counts.dataCount + ", strips:" + offsets.dataCount); } UInt32 offY = 0; List <DngStrip> slices = new List <DngStrip>(); for (UInt32 s = 0; s < offsets.dataCount; s++) { DngStrip slice = new DngStrip(); slice.offset = offsets.getUInt(s); slice.count = counts.getUInt(s); slice.offsetY = offY; if (offY + yPerSlice > height) { slice.h = height - offY; } else { slice.h = yPerSlice; } offY += yPerSlice; if (mFile.isValid(slice.offset, slice.count)) // Only decode if size is valid { slices.Add(slice); } } for (int i = 0; i < slices.Count; i++) { DngStrip slice = slices[i]; TIFFBinaryReader input = new TIFFBinaryReader(mFile.BaseStream, slice.offset, (uint)mFile.BaseStream.Length); iPoint2D size = new iPoint2D((int)width, (int)slice.h); iPoint2D pos = new iPoint2D(0, (int)slice.offsetY); bool big_endian = (raw.endian == Endianness.big); // DNG spec says that if not 8 or 16 bit/sample, always use big endian if (bps != 8 && bps != 16) { big_endian = true; } try { readUncompressedRaw(ref input, size, pos, (int)(mRaw.cpp * width * bps / 8), (int)bps, big_endian ? BitOrder.Jpeg : BitOrder.Plain); } catch (IOException ex) { if (i > 0) { mRaw.errors.Add(ex.Message); } else { throw new RawDecoderException("DNG decoder: IO error occurred in first slice, unable to decode more. Error is: " + ex.Message); } } } } catch (TiffParserException) { throw new RawDecoderException("DNG Decoder: Unsupported format, uncompressed with no strips."); } } else if (compression == 7 || compression == 0x884c) { try { // Let's try loading it as tiles instead mRaw.cpp = (raw.getEntry(TagType.SAMPLESPERPIXEL).getUInt()); if (sample_format != 1) { throw new RawDecoderException("DNG Decoder: Only 16 bit unsigned data supported for compressed data."); } DngDecoderSlices slices = new DngDecoderSlices(mFile, mRaw, compression); if (raw.hasEntry(TagType.TILEOFFSETS)) { UInt32 tilew = raw.getEntry(TagType.TILEWIDTH).getUInt(); UInt32 tileh = raw.getEntry(TagType.TILELENGTH).getUInt(); if (tilew == 0 || tileh == 0) { throw new RawDecoderException("DNG Decoder: Invalid tile size"); } UInt32 tilesX = (uint)(mRaw.dim.x + tilew - 1) / tilew; UInt32 tilesY = (uint)(mRaw.dim.y + tileh - 1) / tileh; UInt32 nTiles = tilesX * tilesY; Tag offsets = raw.getEntry(TagType.TILEOFFSETS); Tag counts = raw.getEntry(TagType.TILEBYTECOUNTS); if (offsets.dataCount != counts.dataCount || offsets.dataCount != nTiles) { throw new RawDecoderException("DNG Decoder: Tile count mismatch: offsets:" + offsets.dataCount + " count:" + counts.dataCount + ", calculated:" + nTiles); } slices.mFixLjpeg = mFixLjpeg; for (UInt32 y = 0; y < tilesY; y++) { for (UInt32 x = 0; x < tilesX; x++) { DngSliceElement e = new DngSliceElement(offsets.getUInt(x + y * tilesX), counts.getUInt(x + y * tilesX), tilew * x, tileh * y); e.mUseBigtable = tilew * tileh > 1024 * 1024; slices.addSlice(e); } } } else { // Strips Tag offsets = raw.getEntry(TagType.STRIPOFFSETS); Tag counts = raw.getEntry(TagType.STRIPBYTECOUNTS); UInt32 yPerSlice = raw.getEntry(TagType.ROWSPERSTRIP).getUInt(); if (counts.dataCount != offsets.dataCount) { throw new RawDecoderException("DNG Decoder: Byte count number does not match strip size: count:" + counts.dataCount + ", stips:" + offsets.dataCount); } if (yPerSlice == 0 || yPerSlice > (UInt32)mRaw.dim.y) { throw new RawDecoderException("DNG Decoder: Invalid y per slice"); } UInt32 offY = 0; for (UInt32 s = 0; s < counts.dataCount; s++) { DngSliceElement e = new DngSliceElement(offsets.getUInt(s), counts.getUInt(s), 0, offY); e.mUseBigtable = yPerSlice * mRaw.dim.y > 1024 * 1024; offY += yPerSlice; if (mFile.isValid(e.byteOffset, e.byteCount)) // Only decode if size is valid { slices.addSlice(e); } } } UInt32 nSlices = (uint)slices.slices.Count; if (nSlices == 0) { throw new RawDecoderException("DNG Decoder: No valid slices found."); } slices.decodeSlice(); if (mRaw.errors.Count >= nSlices) { throw new RawDecoderException("DNG Decoding: Too many errors encountered. Giving up.\nFirst Error:" + mRaw.errors[0]); } } catch (TiffParserException e) { throw new RawDecoderException("DNG Decoder: Unsupported format, tried strips and tiles:" + e.Message); } } else { throw new RawDecoderException("DNG Decoder: Unknown compression: " + compression); } } catch (TiffParserException e) { throw new RawDecoderException("DNG Decoder: Image could not be read:" + e.Message); } Tag as_shot_neutral = mRootIFD.getEntryRecursive(TagType.ASSHOTNEUTRAL); if (as_shot_neutral != null) { if (as_shot_neutral.dataCount == 3) { for (UInt32 i = 0; i < 3; i++) { mRaw.metadata.wbCoeffs[i] = 1.0f / Convert.ToSingle(as_shot_neutral.data[i]); } } } else { Tag as_shot_white_xy = mRootIFD.getEntryRecursive(TagType.ASSHOTWHITEXY); if (as_shot_white_xy != null) { if (as_shot_white_xy.dataCount == 2) { mRaw.metadata.wbCoeffs[0] = as_shot_white_xy.getFloat(0); mRaw.metadata.wbCoeffs[1] = as_shot_white_xy.getFloat(1); mRaw.metadata.wbCoeffs[2] = 1 - mRaw.metadata.wbCoeffs[0] - mRaw.metadata.wbCoeffs[1]; float[] d65_white = { 0.950456F, 1, 1.088754F }; for (UInt32 i = 0; i < 3; i++) { mRaw.metadata.wbCoeffs[i] /= d65_white[i]; } } } } // Crop Tag active_area = raw.getEntry(TagType.ACTIVEAREA); if (active_area != null) { iPoint2D new_size = new iPoint2D(mRaw.dim.x, mRaw.dim.y); if (active_area.dataCount != 4) { throw new RawDecoderException("DNG: active area has " + active_area.dataCount + " values instead of 4"); } active_area.getIntArray(out int[] corners, 4); if (new iPoint2D(corners[1], corners[0]).isThisInside(mRaw.dim)) { if (new iPoint2D(corners[3], corners[2]).isThisInside(mRaw.dim)) { iRectangle2D crop = new iRectangle2D(corners[1], corners[0], corners[3] - corners[1], corners[2] - corners[0]); mRaw.subFrame(crop); } } } Tag origin_entry = raw.getEntry(TagType.DEFAULTCROPORIGIN); Tag size_entry = raw.getEntry(TagType.DEFAULTCROPSIZE); if (origin_entry != null && size_entry != null) { iRectangle2D cropped = new iRectangle2D(0, 0, mRaw.dim.x, mRaw.dim.y); /* Read crop position (sometimes is rational so use float) */ origin_entry.getFloatArray(out float[] tl, 2); if (new iPoint2D((int)tl[0], (int)tl[1]).isThisInside(mRaw.dim)) { cropped = new iRectangle2D((int)tl[0], (int)tl[1], 0, 0); } cropped.dim = mRaw.dim - cropped.pos; /* Read size (sometimes is rational so use float) */ size_entry.getFloatArray(out float[] sz, 2); iPoint2D size = new iPoint2D((int)sz[0], (int)sz[1]); if ((size + cropped.pos).isThisInside(mRaw.dim)) { cropped.dim = size; } if (!cropped.hasPositiveArea()) { throw new RawDecoderException("DNG Decoder: No positive crop area"); } mRaw.subFrame(cropped); if (mRaw.isCFA && cropped.pos.x % 2 == 1) { mRaw.cfa.shiftLeft(1); } if (mRaw.isCFA && cropped.pos.y % 2 == 1) { mRaw.cfa.shiftDown(1); } } if (mRaw.dim.area() <= 0) { throw new RawDecoderException("DNG Decoder: No image left after crop"); } // Apply stage 1 opcodes if (applyStage1DngOpcodes) { if (raw.hasEntry(TagType.OPCODELIST1)) { // Apply stage 1 codes try { //DngOpcodes codes = new DngOpcodes(raw.getEntry(TagType.OPCODELIST1)); //mRaw = codes.applyOpCodes(mRaw); } catch (RawDecoderException e) { // We push back errors from the opcode parser, since the image may still be usable mRaw.errors.Add(e.Message); } } } // Linearization Tag lintable = raw.getEntry(TagType.LINEARIZATIONTABLE); if (lintable != null) { UInt32 len = lintable.dataCount; lintable.getShortArray(out ushort[] table, (int)len); mRaw.setTable(table, (int)len, !uncorrectedRawValues); if (!uncorrectedRawValues) { //TODO Fix //mRaw.sixteenBitLookup(); //mRaw.table = (null); } } // Default white level is (2 ** BitsPerSample) - 1 mRaw.whitePoint = (uint)(1 >> raw.getEntry(TagType.BITSPERSAMPLE).getShort()) - 1; Tag whitelevel = raw.getEntry(TagType.WHITELEVEL); try { mRaw.whitePoint = whitelevel.getUInt(); } catch (Exception) { } // Set black setBlack(raw); //convert to linear value //* //TODO optimize (super slow) double maxVal = Math.Pow(2, mRaw.colorDepth); double coeff = maxVal / (mRaw.whitePoint - mRaw.blackLevelSeparate[0]); Parallel.For(mRaw.mOffset.y, mRaw.dim.y + mRaw.mOffset.y, y => //for (int y = mRaw.mOffset.y; y < mRaw.dim.y + mRaw.mOffset.y; y++) { //int offset = ((y % 2) * 2); int realY = y * mRaw.dim.x; for (int x = mRaw.mOffset.x; x < mRaw.dim.x + mRaw.mOffset.x; x++) { int pos = realY + x; double val; //Linearisation if (mRaw.table != null) { val = mRaw.table.tables[mRaw.rawData[pos]]; } else { val = mRaw.rawData[pos]; } //Black sub //val -= mRaw.blackLevelSeparate[offset + x % 2]; val -= mRaw.blackLevelSeparate[0]; //Rescaling //val /= (mRaw.whitePoint - mRaw.blackLevelSeparate[offset + x % 2]); val *= coeff; //change to take into consideration each individual blacklevel //Clip if (val > maxVal) { val = maxVal; } else if (val < 0) { val = 0; } //val *= maxVal; //rescale to colordepth of the original mRaw.rawData[pos] = (ushort)val; } }); //*/ // Apply opcodes to lossy DNG if (compression == 0x884c && !uncorrectedRawValues) { /* * if (raw.hasEntry(TagType.OPCODELIST2)) * { * // We must apply black/white scaling * mRaw.scaleBlackWhite(); * // Apply stage 2 codes * try * { * DngOpcodes codes = new DngOpcodes(raw.getEntry(TagType.OPCODELIST2)); * mRaw = codes.applyOpCodes(mRaw); * } * catch (RawDecoderException e) * { * // We push back errors from the opcode parser, since the image may still be usable * mRaw.errors.Add(e.Message); * } * mRaw.blackAreas.Clear(); * mRaw.blackLevel = 0; * mRaw.blackLevelSeparate[0] = mRaw.blackLevelSeparate[1] = mRaw.blackLevelSeparate[2] = mRaw.blackLevelSeparate[3] = 0; * mRaw.whitePoint = 65535; * }*/ } return(mRaw); }
bool decodeBlackLevels(IFD raw) { iPoint2D blackdim = new iPoint2D(1, 1); Tag bleveldim = raw.getEntry(TagType.BLACKLEVELREPEATDIM); if (bleveldim != null) { if (bleveldim.dataCount != 2) { return(false); } blackdim = new iPoint2D(bleveldim.getInt(0), bleveldim.getInt(1)); } if (blackdim.x == 0 || blackdim.y == 0) { return(false); } if (raw.getEntry(TagType.BLACKLEVEL) == null) { return(true); } if (mRaw.cpp != 1) { return(false); } Tag black_entry = raw.getEntry(TagType.BLACKLEVEL); if ((int)black_entry.dataCount < blackdim.x * blackdim.y) { throw new RawDecoderException("DNG: BLACKLEVEL entry is too small"); } if (blackdim.x < 2 || blackdim.y < 2) { // We so not have enough to fill all individually, read a single and copy it //TODO check if float float value = black_entry.getFloat(); for (int y = 0; y < 2; y++) { for (int x = 0; x < 2; x++) { mRaw.blackLevelSeparate[y * 2 + x] = (int)value; } } } else { for (int y = 0; y < 2; y++) { for (int x = 0; x < 2; x++) { mRaw.blackLevelSeparate[y * 2 + x] = (int)black_entry.getFloat(y * blackdim.x + x); } } } // DNG Spec says we must add black in deltav and deltah Tag blackleveldeltav = raw.getEntry(TagType.BLACKLEVELDELTAV); if (blackleveldeltav != null) { if ((int)blackleveldeltav.dataCount < mRaw.dim.y) { throw new RawDecoderException("DNG: BLACKLEVELDELTAV array is too small"); } float[] black_sum = { 0.0f, 0.0f }; for (int i = 0; i < mRaw.dim.y; i++) { black_sum[i & 1] += blackleveldeltav.getFloat(i); } for (int i = 0; i < 4; i++) { mRaw.blackLevelSeparate[i] += (int)(black_sum[i >> 1] / mRaw.dim.y * 2.0f); } } Tag blackleveldeltah = raw.getEntry(TagType.BLACKLEVELDELTAH); if (blackleveldeltah != null) { if ((int)blackleveldeltah.dataCount < mRaw.dim.x) { throw new RawDecoderException("DNG: BLACKLEVELDELTAH array is too small"); } float[] black_sum = { 0.0f, 0.0f }; for (int i = 0; i < mRaw.dim.x; i++) { black_sum[i & 1] += blackleveldeltah.getFloat(i); } for (int i = 0; i < 4; i++) { mRaw.blackLevelSeparate[i] += (int)(black_sum[i & 1] / mRaw.dim.x * 2.0f); } } return(true); }
/* Check if the decoder can decode the image from this camera */ /* A RawDecoderException will be thrown if the camera isn't supported */ /* Unknown cameras does NOT generate any specific feedback */ /* This function must be overridden by actual decoders */ public void decodeUncompressed(ref IFD rawIFD, BitOrder order) { UInt32 nslices = rawIFD.getEntry(TagType.STRIPOFFSETS).dataCount; Tag offsets = rawIFD.getEntry(TagType.STRIPOFFSETS); Tag counts = rawIFD.getEntry(TagType.STRIPBYTECOUNTS); UInt32 yPerSlice = rawIFD.getEntry(TagType.ROWSPERSTRIP).getUInt(); Int32 width = rawIFD.getEntry(TagType.IMAGEWIDTH).getInt(); UInt32 height = rawIFD.getEntry(TagType.IMAGELENGTH).getUInt(); int bitPerPixel = rawIFD.getEntry(TagType.BITSPERSAMPLE).getInt(); List <RawSlice> slices = new List <RawSlice>(); UInt32 offY = 0; for (UInt32 s = 0; s < nslices; s++) { RawSlice slice = new RawSlice() { offset = (uint)offsets.data[s], count = (uint)counts.data[s] }; if (offY + yPerSlice > height) { slice.h = height - offY; } else { slice.h = yPerSlice; } offY += yPerSlice; if (mFile.isValid(slice.offset, slice.count)) // Only decode if size is valid { slices.Add(slice); } } if (0 == slices.Count) { throw new RawDecoderException("RAW Decoder: No valid slices found. File probably truncated."); } mRaw.dim.x = width; mRaw.dim.y = (int)offY; mRaw.whitePoint = (uint)(1 << bitPerPixel) - 1; offY = 0; for (int i = 0; i < slices.Count; i++) { RawSlice slice = slices[i]; var stream = mFile.BaseStream; TIFFBinaryReader input; if (mFile is TIFFBinaryReaderRE) { input = new TIFFBinaryReaderRE(mFile.BaseStream, slice.offset, slice.count); } else { input = new TIFFBinaryReader(mFile.BaseStream, slice.offset, slice.count); } iPoint2D size = new iPoint2D(width, (int)slice.h); iPoint2D pos = new iPoint2D(0, (int)offY); bitPerPixel = (int)(slice.count * 8u / (slice.h * width)); try { readUncompressedRaw(ref input, size, pos, width * bitPerPixel / 8, bitPerPixel, order); } catch (RawDecoderException) { if (i > 0) { //TODO add something } else { throw; } } catch (IOException e) { if (i > 0) { //TODO add something } else { throw new RawDecoderException("RAW decoder: IO error occurred in first slice, unable to decode more. Error is: " + e); } } offY += slice.h; } }
public IFD(TIFFBinaryReader fileStream, uint offset, Endianness endian) { this.endian = endian; fileStream.Position = offset; tagNumber = fileStream.ReadUInt16(); tags = new Dictionary <TagType, Tag>(); for (int i = 0; i < tagNumber; i++) { Tag temp = new Tag(); temp.tagId = (TagType)fileStream.ReadUInt16(); //add the displayname temp.displayName = null; temp.dataType = (TiffDataType)fileStream.ReadUInt16(); temp.dataCount = fileStream.ReadUInt32(); //IF makernote, do not parse data //if (temp.tagId == TagType.MAKERNOTE || temp.tagId == TagType.MAKERNOTE_ALT) temp.dataCount = 0; temp.dataOffset = 0; if (((temp.dataCount * temp.getTypeSize(temp.dataType) > 4))) { temp.dataOffset = fileStream.ReadUInt32(); } //Get the tag data temp.data = new Object[temp.dataCount]; long firstPosition = fileStream.Position; if (temp.dataOffset > 1) { fileStream.Position = temp.dataOffset; //todo check if correct } if (temp.tagId != TagType.MAKERNOTE && temp.tagId != TagType.MAKERNOTE_ALT) { for (int j = 0; j < temp.dataCount; j++) { switch (temp.dataType) { case TiffDataType.BYTE: case TiffDataType.UNDEFINED: case TiffDataType.ASCII: case TiffDataType.OFFSET: temp.data[j] = fileStream.ReadByte(); break; case TiffDataType.SHORT: temp.data[j] = fileStream.ReadUInt16(); break; case TiffDataType.LONG: temp.data[j] = fileStream.ReadUInt32(); break; case TiffDataType.RATIONAL: temp.data[j] = fileStream.ReadDouble(); break; case TiffDataType.SBYTE: temp.data[j] = fileStream.ReadSByte(); break; case TiffDataType.SSHORT: temp.data[j] = fileStream.ReadInt16(); //if (temp.dataOffset == 0 && temp.dataCount == 1) fileStream.ReadInt16(); break; case TiffDataType.SLONG: temp.data[j] = fileStream.ReadInt32(); break; case TiffDataType.SRATIONAL: //Because the nikonmakernote is broken with the tag 0x19 wich is double but offset of zero. //TODO remove this Fix if (temp.dataOffset == 0) { temp.data[j] = .0; } else { temp.data[j] = fileStream.ReadDouble(); } break; case TiffDataType.FLOAT: temp.data[j] = fileStream.ReadSingle(); break; case TiffDataType.DOUBLE: temp.data[j] = fileStream.ReadDouble(); break; } } }//Special tag switch (temp.tagId) { case TagType.DNGPRIVATEDATA: { try { IFD maker_ifd = parseDngPrivateData(temp); if (maker_ifd != null) { subIFD.Add(maker_ifd); } temp.data = null; } catch (TiffParserException) { // Unparsable private data are added as entries } catch (IOException) { // Unparsable private data are added as entries } } break; case TagType.MAKERNOTE: case TagType.MAKERNOTE_ALT: { try { //save current position long pos = fileStream.Position; IFD makernote = parseMakerNote(fileStream, temp.dataOffset, endian); if (makernote != null) { subIFD.Add(makernote); } //correct here fileStream.BaseStream.Position = pos; //return to current position } catch (TiffParserException) { // Unparsable makernotes are added as entries } catch (IOException) { // Unparsable makernotes are added as entries } } break; case TagType.FUJI_RAW_IFD: if (temp.dataType == TiffDataType.OFFSET) // FUJI - correct type { temp.dataType = TiffDataType.LONG; } goto case TagType.SUBIFDS; case TagType.SUBIFDS: case TagType.EXIFIFDPOINTER: case TagType.NIKONTHUMB: long p = fileStream.Position; try { for (Int32 k = 0; k < temp.dataCount; k++) { subIFD.Add(new IFD(fileStream, Convert.ToUInt32(temp.data[k]), endian, depth)); } } catch (TiffParserException) { // Unparsable subifds are added as entries } catch (IOException) { // Unparsable subifds are added as entries } fileStream.BaseStream.Position = p; break; } //transform data ToString if (temp.dataType == TiffDataType.ASCII) { //remove \0 if any if ((byte)temp.data[temp.dataCount - 1] == 0) { temp.data[temp.dataCount - 1] = (byte)' '; } string t = Encoding.ASCII.GetString(temp.data.Cast <byte>().ToArray()); temp.data = new Object[1]; temp.data[0] = t; } if (temp.dataOffset > 1) { fileStream.BaseStream.Position = firstPosition; } else if (temp.dataOffset == 0) { int k = (int)temp.dataCount * temp.getTypeSize(temp.dataType); if (k < 4) { fileStream.ReadBytes(4 - k); } } /*else * { * temp.dataCount = 0; * temp.data = null; * }*/ if (!tags.ContainsKey(temp.tagId)) { tags.Add(temp.tagId, temp); } else { Debug.WriteLine("tags already exist"); } } nextOffset = fileStream.ReadUInt16(); }
/* This will attempt to parse makernotes and return it as an IFD */ IFD parseMakerNote(TIFFBinaryReader reader, uint off, Endianness parent_end) { IFD maker_ifd = null; uint offset = 0; TIFFBinaryReader mFile = null; reader.Position = off; byte[] data = reader.ReadBytes(100); // Pentax makernote starts with AOC\0 - If it's there, skip it if (data[0] == 0x41 && data[1] == 0x4f && data[2] == 0x43 && data[3] == 0) { //data = data.Skip(4).ToArray(); offset += 4; } // Pentax also has "PENTAX" at the start, makernote starts at 8 if (data[0 + offset] == 0x50 && data[1 + offset] == 0x45 && data[2 + offset] == 0x4e && data[3 + offset] == 0x54 && data[4 + offset] == 0x41 && data[5 + offset] == 0x58) { mFile = new TIFFBinaryReader(reader.BaseStream, offset + off, (uint)data.Length); parent_end = getTiffEndianness(data.Skip(8).ToArray()); if (parent_end == Endianness.unknown) { throw new TiffParserException("Cannot determine Pentax makernote endianness"); } //data = data.Skip(10).ToArray(); offset += 10; // Check for fuji signature in else block so we don't accidentally leak FileMap } else if (Common.memcmp(ref fuji_signature, ref data)) { offset = 12; mFile = new TIFFBinaryReader(reader.BaseStream, offset + off, (uint)data.Length); } else if (Common.memcmp(ref nikon_v3_signature, ref data)) { //offset = 10; offset = 10; // Read endianness if (data[0 + offset] == 0x49 && data[1 + offset] == 0x49) { parent_end = Endianness.little; mFile = new TIFFBinaryReader(reader.BaseStream, offset + off, (uint)data.Length); offset = 8; } else if (data[0 + offset] == 0x4D && data[1 + offset] == 0x4D) { parent_end = Endianness.big; mFile = new TIFFBinaryReaderRE(reader.BaseStream, offset + off, (uint)data.Length); offset = 8; } } // Panasonic has the word Exif at byte 6, a complete Tiff header starts at byte 12 // This TIFF is 0 offset based if (data[6] == 0x45 && data[7] == 0x78 && data[8] == 0x69 && data[9] == 0x66) { parent_end = getTiffEndianness(data.Skip(12).ToArray()); if (parent_end == Endianness.unknown) { throw new TiffParserException("Cannot determine Panasonic makernote endianness"); } offset = 20; } // Some have MM or II to indicate endianness - read that if (data[0] == 0x49 && data[1] == 0x49) { offset += 2; parent_end = Endianness.little; } else if (data[0] == 0x4D && data[1] == 0x4D) { parent_end = Endianness.big; offset += 2; } // Olympus starts the makernote with their own name, sometimes truncated if (Common.strncmp(data, "OLYMP", 5)) { offset += 8; if (Common.strncmp(data, "OLYMPUS", 7)) { offset += 4; } } // Epson starts the makernote with its own name if (Common.strncmp(data, "EPSON", 5)) { offset += 8; } // Attempt to parse the rest as an IFD try { if (mFile == null) { if (parent_end == Endianness.little) { mFile = new TIFFBinaryReader(reader.BaseStream, offset + off, (uint)data.Length); } else if (parent_end == Endianness.big) { mFile = new TIFFBinaryReaderRE(reader.BaseStream, offset + off, (uint)data.Length); } } /* if (parent_end == getHostEndianness()) * maker_ifd = new IFD(mFile, offset, depth); * else * maker_ifd = new IFDBE(mFile, offset, depth);*/ maker_ifd = new IFD(mFile, offset, endian, depth); } catch (Exception e) { Debug.WriteLine(e.Message); return(null); } // If the structure cannot be read, a TiffParserException will be thrown. return(maker_ifd); }
public RawDecoder getDecoder() { if (rootIFD == null) { parseData(); } List <IFD> potentials = new List <IFD>(); potentials = rootIFD.getIFDsWithTag(TagType.DNGVERSION); /* Copy, so we can pass it on and not have it destroyed with ourselves */ IFD root = rootIFD; if (potentials.Count != 0) { // We have a dng image entry IFD t = potentials[0]; t.tags.TryGetValue(TagType.DNGVERSION, out Tag tag); object[] c = tag.data; if (Convert.ToInt32(c[0]) > 1) { throw new TiffParserException("DNG version too new."); } rootIFD = null; return(new DngDecoder(root, ref reader)); } potentials = rootIFD.getIFDsWithTag(TagType.MAKE); if (potentials.Count > 0) { // We have make entry foreach (IFD i in potentials) { i.tags.TryGetValue(TagType.MAKE, out Tag tag); string make = tag.dataAsString; make = make.Trim(); //remove trailing \0 if any string model = ""; i.tags.TryGetValue(TagType.MODEL, out Tag tagModel); if (tagModel != null) { model = tagModel.dataAsString; model = make.Trim(); } switch (make) { /* * case "Canon": * rootIFD = null; * return new Cr2Decoder(root, reader); * case "FUJIFILM": * rootIFD = null; * return new RafDecoder(root, reader);*/ case "NIKON CORPORATION": case "NIKON": rootIFD = null; return(new NefDecoder(ref root, reader)); /* * case "OLYMPUS IMAGING CORP.": * case "OLYMPUS CORPORATION": * case "OLYMPUS OPTICAL CO.,LTD": * rootIFD = null; * return new OrfDecoder(root, reader); * case "SONY": * rootIFD = null; * return new ArwDecoder(root, reader); * case "PENTAX Corporation": * case "RICOH IMAGING COMPANY, LTD.": * case "PENTAX": * rootIFD = null; * return new PefDecoder(root, reader); * case "Panasonic": * case "LEICA": * rootIFD = null; * return new Rw2Decoder(root, reader); * case "SAMSUNG": * rootIFD = null; * return new SrwDecoder(root, reader); * case "Mamiya-OP Co.,Ltd.": * rootIFD = null; * return new MefDecoder(root, reader); * case "Kodak": * rootIFD = null; * if (String.Compare(model, "DCS560C") == 0) * return new Cr2Decoder(root, reader); * else * return new DcrDecoder(root, reader); * case "KODAK": * rootIFD = null; * return new DcsDecoder(root, reader); * case "EASTMAN KODAK COMPANY": * rootIFD = null; * return new KdcDecoder(root, reader); * case "SEIKO EPSON CORP.": * rootIFD = null; * return new ErfDecoder(root, reader); * case "Hasselblad": * rootIFD = null; * return new ThreefrDecoder(root, reader); * case "Leaf": * rootIFD = null; * return new MosDecoder(root, reader); * case "Phase One A/S": * rootIFD = null; * return new MosDecoder(root, reader); */ } } /* * // Last ditch effort to identify Leaf cameras that don't have a Tiff Make set * potentials = rootIFD.getIFDsWithTag(TagType.SOFTWARE); * if (potentials.Count > 0) * { * potentials[0].tags.TryGetValue((ushort)TagType.SOFTWARE, out Tag tag); * string software = tag.dataAsString; * software = software.Trim(); * if (String.Compare(software, "Camera Library") == 0) * { * rootIFD = null; * return new MosDecoder(root, reader); * } * }*/ } //default as astandard tiff rootIFD = null; return(new TiffDecoder(root, ref reader)); //TODO add detection of Tiff throw new TiffParserException("No decoder found. Sorry."); }