GetMemorySize() public static method

Returns the size in memory of a region with the given extents and pixel format with consecutive memory layout.
In case that the format is non-compressed, this simply returns width * height * depth * PixelConverter.GetNumElemBytes(format). In the compressed case, this does serious magic.
public static GetMemorySize ( int width, int height, int depth, PixelFormat format ) : int
width int Width of the area
height int Height of the area
depth int Depth of the area
format PixelFormat Format of the area
return int
Example #1
0
        public PixelBox GetPixelBox(int face, int mipmap)
        {
            if (mipmap > numMipMaps)
            {
                throw new IndexOutOfRangeException();
            }
            if (face > this.NumFaces)
            {
                throw new IndexOutOfRangeException();
            }
            // Calculate mipmap offset and size
            int width    = this.Width;
            int height   = this.Height;
            int depth    = this.Depth;
            int faceSize = 0; // Size of one face of the image
            int offset   = 0;

            for (int mip = 0; mip < mipmap; ++mip)
            {
                faceSize = PixelUtil.GetMemorySize(width, height, depth, this.Format);
                /// Skip all faces of this mipmap
                offset += faceSize * this.NumFaces;
                /// Half size in each dimension
                if (width != 1)
                {
                    width /= 2;
                }
                if (height != 1)
                {
                    height /= 2;
                }
                if (depth != 1)
                {
                    depth /= 2;
                }
            }
            // We have advanced to the desired mipmap, offset to right face
            faceSize = PixelUtil.GetMemorySize(width, height, depth, this.Format);
            offset  += faceSize * face;
            // Return subface as pixelbox
            IntPtr newBufPtr = Marshal.UnsafeAddrOfPinnedArrayElement(buffer, offset);

            return(new PixelBox(width, height, depth, this.Format, newBufPtr));
        }
Example #2
0
        public static int CalculateSize(int mipmaps, int faces, int width, int height, int depth, PixelFormat format)
        {
            int size = 0;

            for (int mip = 0; mip <= mipmaps; ++mip)
            {
                size += PixelUtil.GetMemorySize(width, height, depth, format) * faces;
                if (width != 1)
                {
                    width /= 2;
                }
                if (height != 1)
                {
                    height /= 2;
                }
                if (depth != 1)
                {
                    depth /= 2;
                }
            }
            return(size);
        }
Example #3
0
        public override Codec.DecodeResult Decode(Stream input)
        {
            using (var br = new BinaryReader(input))
            {
                // Read 4 character code
                var fileType = br.ReadInt32();
                using (var wrap = BufferBase.Wrap(fileType, 2))
                {
                    _flipEndian(wrap, sizeof(uint), 1);
                }

                if (FOURCC('D', 'D', 'S', ' ') != fileType)
                {
                    throw new AxiomException("This is not a DDS file!");
                }

                // Read header in full
                var header = DDSHeader.Read(br);

                // Endian flip if required, all 32-bit values
                using (var wrap = BufferBase.Wrap(header, Memory.SizeOf(typeof(DDSHeader))))
                {
                    _flipEndian(wrap, 4, Memory.SizeOf(typeof(DDSHeader)) / 4);
                }

                // Check some sizes
                if (header.size != DDS_HEADER_SIZE)
                {
                    throw new AxiomException("DDS header size mismatch!");
                }

                if (header.pixelFormat.size != DDS_PIXELFORMAT_SIZE)
                {
                    throw new AxiomException("DDS header size mismatch!");
                }

                var imgData = new ImageData();

                imgData.depth  = 1; // (deal with volume later)
                imgData.width  = header.width;
                imgData.height = header.height;
                var numFaces = 1; // assume one face until we know otherwise

                if ((header.caps.caps1 & DDSCAPS_MIPMAP) != 0)
                {
                    imgData.numMipMaps = header.mipMapCount - 1;
                }
                else
                {
                    imgData.numMipMaps = 0;
                }

                imgData.flags = 0;

                var decompressDXT = false;
                // Figure out basic image type
                if ((header.caps.caps2 & DDSCAPS2_CUBEMAP) != 0)
                {
                    imgData.flags |= ImageFlags.CubeMap;
                    numFaces       = 6;
                }
                else if ((header.caps.caps2 & DDSCAPS2_VOLUME) != 0)
                {
                    imgData.flags |= ImageFlags.Volume;
                    imgData.depth  = header.depth;
                }
                // Pixel format
                var sourceFormat = PixelFormat.Unknown;

                if ((header.pixelFormat.flags & DDPF_FOURCC) != 0)
                {
                    sourceFormat = _convertFourCCFormat(header.pixelFormat.fourCC);
                }
                else
                {
                    sourceFormat = _convertPixelFormat(header.pixelFormat.rgbBits, header.pixelFormat.redMask,
                                                       header.pixelFormat.greenMask, header.pixelFormat.blueMask,
                                                       (header.pixelFormat.flags & DDPF_ALPHAPIXELS) != 0
                                                            ? header.pixelFormat.alphaMask
                                                            : 0);
                }

                if (PixelUtil.IsCompressed(sourceFormat))
                {
                    if (!Root.Instance.RenderSystem.Capabilities.HasCapability(Capabilities.TextureCompressionDXT))
                    {
                        // We'll need to decompress
                        decompressDXT = true;
                        // Convert format
                        switch (sourceFormat)
                        {
                        case PixelFormat.DXT1:
                            // source can be either 565 or 5551 depending on whether alpha present
                            // unfortunately you have to read a block to figure out which
                            // Note that we upgrade to 32-bit pixel formats here, even
                            // though the source is 16-bit; this is because the interpolated
                            // values will benefit from the 32-bit results, and the source
                            // from which the 16-bit samples are calculated may have been
                            // 32-bit so can benefit from this.
                            var block = DXTColorBlock.Read(br);
                            using (var wrap = BufferBase.Wrap(block.colour_0, sizeof(ushort)))
                            {
                                _flipEndian(wrap, sizeof(ushort), 1);
                            }

                            using (var wrap = BufferBase.Wrap(block.colour_1, sizeof(ushort)))
                            {
                                _flipEndian(wrap, sizeof(ushort), 1);
                            }
                            // skip back since we'll need to read this again
                            br.BaseStream.Seek(0 - (long)Memory.SizeOf(typeof(DXTColorBlock)), SeekOrigin.Current);
                            // colour_0 <= colour_1 means transparency in DXT1
                            if (block.colour_0 <= block.colour_1)
                            {
                                imgData.format = PixelFormat.BYTE_RGBA;
                            }
                            else
                            {
                                imgData.format = PixelFormat.BYTE_RGB;
                            }
                            break;

                        case PixelFormat.DXT2:
                        case PixelFormat.DXT3:
                        case PixelFormat.DXT4:
                        case PixelFormat.DXT5:
                            // full alpha present, formats vary only in encoding
                            imgData.format = PixelFormat.BYTE_RGBA;
                            break;

                        default:
                            // all other cases need no special format handling
                            break;
                        }
                    }
                    else
                    {
                        // Use original format
                        imgData.format = sourceFormat;
                        // Keep DXT data compressed
                        imgData.flags |= ImageFlags.Compressed;
                    }
                }
                else // not compressed
                {
                    // Don't test against DDPF_RGB since greyscale DDS doesn't set this
                    // just derive any other kind of format
                    imgData.format = sourceFormat;
                }

                // Calculate total size from number of mipmaps, faces and size
                imgData.size = Image.CalculateSize(imgData.numMipMaps, numFaces, imgData.width, imgData.height, imgData.depth, imgData.format);

                // Now deal with the data
                var dest       = new byte[imgData.size];
                var destBuffer = BufferBase.Wrap(dest);

                // all mips for a face, then each face
                for (var i = 0; i < numFaces; ++i)
                {
                    var width  = imgData.width;
                    var height = imgData.height;
                    var depth  = imgData.depth;

                    for (var mip = 0; mip <= imgData.numMipMaps; ++mip)
                    {
                        var dstPitch = width * PixelUtil.GetNumElemBytes(imgData.format);

                        if (PixelUtil.IsCompressed(sourceFormat))
                        {
                            // Compressed data
                            if (decompressDXT)
                            {
                                DXTColorBlock             col;
                                DXTInterpolatedAlphaBlock iAlpha;
                                DXTExplicitAlphaBlock     eAlpha;
                                // 4x4 block of decompressed colour
                                var tempColours     = new ColorEx[16];
                                var destBpp         = PixelUtil.GetNumElemBytes(imgData.format);
                                var sx              = Utility.Min(width, 4);
                                var sy              = Utility.Min(height, 4);
                                var destPitchMinus4 = dstPitch - destBpp * sx;
                                // slices are done individually
                                for (var z = 0; z < depth; ++z)
                                {
                                    // 4x4 blocks in x/y
                                    for (var y = 0; y < height; y += 4)
                                    {
                                        for (var x = 0; x < width; x += 4)
                                        {
                                            if (sourceFormat == PixelFormat.DXT2 || sourceFormat == PixelFormat.DXT3)
                                            {
                                                // explicit alpha
                                                eAlpha = DXTExplicitAlphaBlock.Read(br);
                                                using (var wrap = BufferBase.Wrap(eAlpha.alphaRow, eAlpha.alphaRow.Length * sizeof(ushort)))
                                                {
                                                    _flipEndian(wrap, sizeof(ushort), 4);
                                                }
                                                _unpackDXTAlpha(eAlpha, tempColours);
                                            }
                                            else if (sourceFormat == PixelFormat.DXT4 || sourceFormat == PixelFormat.DXT5)
                                            {
                                                // interpolated alpha
                                                iAlpha = DXTInterpolatedAlphaBlock.Read(br);
                                                using (var wrap = BufferBase.Wrap(iAlpha.alpha_0, 1))
                                                {
                                                    _flipEndian(wrap, sizeof(ushort), 1);
                                                }

                                                using (var wrap = BufferBase.Wrap(iAlpha.alpha_1, 1))
                                                {
                                                    _flipEndian(wrap, sizeof(ushort), 1);
                                                }
                                                _unpackDXTAlpha(iAlpha, tempColours);
                                            }
                                            // always read colour
                                            col = DXTColorBlock.Read(br);

                                            using (var wrap = BufferBase.Wrap(col.colour_0, sizeof(ushort)))
                                            {
                                                _flipEndian(wrap, sizeof(ushort), 1);
                                            }

                                            using (var wrap = BufferBase.Wrap(col.colour_1, sizeof(ushort)))
                                            {
                                                _flipEndian(wrap, sizeof(ushort), 1);
                                            }
                                            _unpackDXTColor(sourceFormat, col, tempColours);

                                            // write 4x4 block to uncompressed version
                                            for (var by = 0; by < sy; ++by)
                                            {
                                                for (var bx = 0; bx < sx; ++bx)
                                                {
                                                    PixelConverter.PackColor(tempColours[by * 4 + bx], imgData.format, destBuffer);
                                                    destBuffer += destBpp;
                                                }
                                                // advance to next row
                                                destBuffer += destPitchMinus4;
                                            }
                                            // next block. Our dest pointer is 4 lines down
                                            // from where it started
                                            if (x + 4 >= width)
                                            {
                                                // Jump back to the start of the line
                                                destBuffer += -destPitchMinus4;
                                            }
                                            else
                                            {
                                                // Jump back up 4 rows and 4 pixels to the
                                                // right to be at the next block to the right
                                                destBuffer += -(dstPitch * sy + destBpp * sx);
                                            }
                                        }
                                    }
                                }
                            }
                            else
                            {
                                // load directly
                                // DDS format lies! sizeOrPitch is not always set for DXT!!
                                var dxtSize = PixelUtil.GetMemorySize(width, height, depth, imgData.format);
                                using (var src = BufferBase.Wrap(br.ReadBytes(dxtSize)))
                                {
                                    Memory.Copy(src, destBuffer, dxtSize);
                                }
                                destBuffer += dxtSize;
                            }
                        }
                        else
                        {
                            // Final data - trim incoming pitch
                            int srcPitch;
                            if ((header.flags & DDSD_PITCH) != 0)
                            {
                                srcPitch = header.sizeOrPitch / Utility.Max(1, mip * 2);
                            }
                            else
                            {
                                // assume same as final pitch
                                srcPitch = dstPitch;
                            }
                            Contract.Requires(dstPitch <= srcPitch);
                            var srcAdvance = (long)(srcPitch - dstPitch);

                            for (var z = 0; z < imgData.depth; ++z)
                            {
                                for (var y = 0; y < imgData.height; ++y)
                                {
                                    using (var src = BufferBase.Wrap(br.ReadBytes(dstPitch)))
                                    {
                                        Memory.Copy(src, destBuffer, dstPitch);
                                    }

                                    if (srcAdvance > 0)
                                    {
                                        br.BaseStream.Seek(srcAdvance, SeekOrigin.Current);
                                    }

                                    destBuffer += dstPitch;
                                }
                            }
                        }

                        // Next mip
                        if (width != 1)
                        {
                            width /= 2;
                        }

                        if (height != 1)
                        {
                            height /= 2;
                        }

                        if (depth != 1)
                        {
                            depth /= 2;
                        }
                    }
                }

                destBuffer.Dispose();
                return(new DecodeResult(new MemoryStream(dest), imgData));
            }
        }
        public override object Decode(Stream input, Stream output, params object[] args)
        {
            ImageData data = new ImageData();

            int imageID;
            int format, bytesPerPixel, imageType;

            // create and bind a new image
            Il.ilGenImages(1, out imageID);
            Il.ilBindImage(imageID);

            // Put it right side up
            Il.ilEnable(Il.IL_ORIGIN_SET);
            Il.ilSetInteger(Il.IL_ORIGIN_MODE, Il.IL_ORIGIN_UPPER_LEFT);

            // Keep DXTC(compressed) data if present
            Il.ilSetInteger(Il.IL_KEEP_DXTC_DATA, Il.IL_TRUE);

            // create a temp buffer and write the stream into it
            byte[] buffer = new byte[input.Length];
            input.Read(buffer, 0, buffer.Length);

            // load the data into DevIL
            Il.ilLoadL(this.ILType, buffer, buffer.Length);

            // check for an error
            int ilError = Il.ilGetError();

            if (ilError != Il.IL_NO_ERROR)
            {
                throw new AxiomException("Error while decoding image data: '{0}'", Ilu.iluErrorString(ilError));
            }

            format    = Il.ilGetInteger(Il.IL_IMAGE_FORMAT);
            imageType = Il.ilGetInteger(Il.IL_IMAGE_TYPE);
            //bytesPerPixel = Math.Max(Il.ilGetInteger(Il.IL_IMAGE_BPC),
            //                         Il.ilGetInteger(Il.IL_IMAGE_BYTES_PER_PIXEL));

            // Convert image if ImageType is incompatible with us (double or long)
            if (imageType != Il.IL_BYTE && imageType != Il.IL_UNSIGNED_BYTE &&
                imageType != Il.IL_FLOAT &&
                imageType != Il.IL_UNSIGNED_SHORT && imageType != Il.IL_SHORT)
            {
                Il.ilConvertImage(format, Il.IL_FLOAT);
                imageType = Il.IL_FLOAT;
            }
            // Converted paletted images
            if (format == Il.IL_COLOUR_INDEX)
            {
                Il.ilConvertImage(Il.IL_BGRA, Il.IL_UNSIGNED_BYTE);
                format    = Il.IL_BGRA;
                imageType = Il.IL_UNSIGNED_BYTE;
            }

            bytesPerPixel = Il.ilGetInteger(Il.IL_IMAGE_BYTES_PER_PIXEL);

            // populate the image data
            data.format     = ILUtil.ConvertFromILFormat(format, imageType);
            data.width      = Il.ilGetInteger(Il.IL_IMAGE_WIDTH);
            data.height     = Il.ilGetInteger(Il.IL_IMAGE_HEIGHT);
            data.depth      = Il.ilGetInteger(Il.IL_IMAGE_DEPTH);
            data.numMipMaps = Il.ilGetInteger(Il.IL_NUM_MIPMAPS);
            data.flags      = 0;

            if (data.format == PixelFormat.Unknown)
            {
                Il.ilDeleteImages(1, ref imageID);
                throw new AxiomException("Unsupported DevIL format: ImageFormat = {0:x} ImageType = {1:x}", format, imageType);
            }

            // Check for cubemap
            // int cubeflags = Il.ilGetInteger(Il.IL_IMAGE_CUBEFLAGS);
            int numFaces = Il.ilGetInteger(Il.IL_NUM_IMAGES) + 1;

            if (numFaces == 6)
            {
                data.flags |= ImageFlags.CubeMap;
            }
            else
            {
                numFaces = 1; // Support only 1 or 6 face images for now
            }
            // Keep DXT data (if present at all and the GPU supports it)
            int dxtFormat = Il.ilGetInteger(Il.IL_DXTC_DATA_FORMAT);

            if (dxtFormat != Il.IL_DXT_NO_COMP &&
                Root.Instance.RenderSystem.Caps.CheckCap(Axiom.Graphics.Capabilities.TextureCompressionDXT))
            {
                data.format = ILUtil.ConvertFromILFormat(dxtFormat, imageType);
                data.flags |= ImageFlags.Compressed;

                // Validate that this devil version saves DXT mipmaps
                if (data.numMipMaps > 0)
                {
                    Il.ilBindImage(imageID);
                    Il.ilActiveMipmap(1);
                    if (Il.ilGetInteger(Il.IL_DXTC_DATA_FORMAT) != dxtFormat)
                    {
                        data.numMipMaps = 0;
                        LogManager.Instance.Write("Warning: Custom mipmaps for compressed image were ignored because they are not loaded by this DevIL version");
                    }
                }
            }

            // Calculate total size from number of mipmaps, faces and size
            data.size = Image.CalculateSize(data.numMipMaps, numFaces,
                                            data.width, data.height,
                                            data.depth, data.format);

            // set up buffer for the decoded data
            buffer = new byte[data.size];
            // Pin the buffer, so we can use our PixelBox methods on it
            GCHandle bufGCHandle = new GCHandle();

            bufGCHandle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
            IntPtr bufPtr = bufGCHandle.AddrOfPinnedObject();

            int offset = 0;

            // Dimensions of current mipmap
            int width  = data.width;
            int height = data.height;
            int depth  = data.depth;

            // Transfer data
            for (int mip = 0; mip <= data.numMipMaps; ++mip)
            {
                for (int i = 0; i < numFaces; ++i)
                {
                    Il.ilBindImage(imageID);
                    if (numFaces > 1)
                    {
                        Il.ilActiveImage(i);
                    }
                    if (data.numMipMaps > 0)
                    {
                        Il.ilActiveMipmap(mip);
                    }
                    /// Size of this face
                    int imageSize = PixelUtil.GetMemorySize(width, height, depth, data.format);
                    if ((data.flags & ImageFlags.Compressed) != 0)
                    {
                        // Compare DXT size returned by DevIL with our idea of the compressed size
                        if (imageSize == Il.ilGetDXTCData(IntPtr.Zero, 0, dxtFormat))
                        {
                            // Retrieve data from DevIL
                            byte[] tmpBuffer = new byte[imageSize];
                            Il.ilGetDXTCData(tmpBuffer, imageSize, dxtFormat);
                            // Copy the data into our output buffer
                            Array.Copy(tmpBuffer, 0, buffer, offset, tmpBuffer.Length);
                        }
                        else
                        {
                            LogManager.Instance.Write("Warning: compressed image size mismatch, devilsize={0} oursize={1}",
                                                      Il.ilGetDXTCData(IntPtr.Zero, 0, dxtFormat), imageSize);
                        }
                    }
                    else
                    {
                        /// Retrieve data from DevIL
                        PixelBox dst = new PixelBox(width, height, depth, data.format, bufPtr);
                        dst.Offset = offset;
                        ILUtil.ToAxiom(dst);
                    }
                    offset += imageSize;
                }

                /// Next mip
                if (width != 1)
                {
                    width /= 2;
                }
                if (height != 1)
                {
                    height /= 2;
                }
                if (depth != 1)
                {
                    depth /= 2;
                }
            }

            // Restore IL state
            Il.ilDisable(Il.IL_ORIGIN_SET);
            Il.ilDisable(Il.IL_FORMAT_SET);

            // we won't be needing this anymore
            Il.ilDeleteImages(1, ref imageID);

            output.Write(buffer, 0, buffer.Length);


            // Free the buffer we allocated for the conversion.
            // I used bufPtr to store my data while I converted it.
            // I need to free it here.  This invalidates bufPtr.
            // My data has already been copied to output.
            if (bufGCHandle.IsAllocated)
            {
                bufGCHandle.Free();
            }

            return(data);
        }