Exemple #1
0
 void setBlack(IFD raw)
 {
     if (raw.hasEntry(TagType.MASKEDAREAS))
     {
         if (decodeMaskedAreas(raw))
         {
             return;
         }
     }
     if (raw.getEntry(TagType.BLACKLEVEL) != null)
     {
         decodeBlackLevels(raw);
     }
 }
Exemple #2
0
        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);
        }
Exemple #3
0
        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);
        }