Example #1
0
        /// <summary>
        /// Encodes an image into the BC1 format.
        /// </summary>
        /// <param name="image">The image to encode.</param>
        /// <param name="rChan">The channel to pull red values from.</param>
        /// <param name="gChan">The channel to pull green values from.</param>
        /// <param name="bChan">The channel to pull blue values from.</param>
        /// <param name="aChan">The channel to pull alpha values from.</param>
        /// <returns>The encoded BC1 image data.</returns>
        public BCnImage<BC1Block> EncodeBC1A( FloatImage image,
			int rChan = 0, int gChan = 1, int bChan = 2, int aChan = 3 )
        {
            if( aChan == -1 )
                throw new ArgumentOutOfRangeException( "aChan" );
            //rest of the cases caught in InternalEncodeBC1

            return InternalEncodeBC1( image, rChan, gChan, bChan, aChan );
        }
Example #2
0
        /// <summary>
        /// Encodes an image into the BC2 format.
        /// </summary>
        /// <param name="image">The image to encode.</param>
        /// <param name="rChan">The channel to pull red values from.</param>
        /// <param name="gChan">The channel to pull green values from.</param>
        /// <param name="bChan">The channel to pull blue values from.</param>
        /// <param name="aChan">The channel to pull alpha values from.</param>
        /// <returns>The encoded BC2 image data.</returns>
        public BCnImage<BC2Block> EncodeBC2( FloatImage image,
			int rChan = 0, int gChan = 1, int bChan = 2, int aChan = 3 )
        {
            if( image == null )
                throw new ArgumentNullException( "image" );
            if( rChan < 0 || rChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "rChan" );
            if( gChan < 0 || gChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "gChan" );
            if( bChan < 0 || bChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "bChan" );
            if( aChan < 0 || aChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "aChan" );

            var ret = new BCnImage<BC2Block>( image.Width, image.Height );

            ImageEncodingHelper.EncodeBlocks( ret,
                () =>
                {
                    var encBC1 = new BC1BlockEncoder();
                    var encBC2A = new BC2ABlockEncoder();

                    encBC1.DitherRgb = DitherRgb;
                    encBC2A.Dither = DitherAlpha;

                    return ( chanData, index, pitch ) =>
                    {
                        encBC1.LoadBlock(
                            chanData[0], index,
                            chanData[1], index,
                            chanData[2], index,
                            pitch );
                        encBC2A.LoadBlock( chanData[3], index, pitch );

                        BC2Block block;

                        block.Rgb = encBC1.Encode();
                        block.A = encBC2A.Encode();

                        return block;
                    };
                },
                image, new int[] { rChan, gChan, bChan, aChan } );

            return ret;
        }
Example #3
0
        /// <summary>
        /// Encodes an image into the signed BC5 format.
        /// </summary>
        /// <param name="image">The image to encode.</param>
        /// <param name="rChan">The channel to pull red values from.</param>
        /// <param name="gChan">The channel to pull green values from.</param>
        /// <returns>The encoded signed BC5 image data.</returns>
        public BCnImage <BC5SBlock> EncodeBC5S(FloatImage image,
                                               int rChan = 0, int gChan = 1)
        {
            if (image == null)
            {
                throw new ArgumentNullException("image");
            }
            if (rChan < 0 || rChan >= image.ChannelCount)
            {
                throw new ArgumentOutOfRangeException("rChan");
            }
            if (gChan < 0 || gChan >= image.ChannelCount)
            {
                throw new ArgumentOutOfRangeException("gChan");
            }

            var ret = new BCnImage <BC5SBlock>(image.Width, image.Height);

            ImageEncodingHelper.EncodeBlocks(ret,
                                             () =>
            {
                var encBC4 = new BC4BlockEncoder();

                return((chanData, index, pitch) =>
                {
                    BC5SBlock block;

                    encBC4.LoadBlock(chanData[0], index, pitch);
                    block.R = encBC4.EncodeSigned();

                    encBC4.LoadBlock(chanData[1], index, pitch);
                    block.G = encBC4.EncodeSigned();

                    return block;
                });
            },
                                             image, new int[] { rChan, gChan });

            return(ret);
        }
Example #4
0
 /// <summary>
 /// Encodes an image into the BC1 format.
 /// </summary>
 /// <param name="image">The image to encode.</param>
 /// <param name="rChan">The channel to pull red values from.</param>
 /// <param name="gChan">The channel to pull green values from.</param>
 /// <param name="bChan">The channel to pull blue values from.</param>
 /// <returns>The encoded BC1 image data.</returns>
 public BCnImage <BC1Block> EncodeBC1(FloatImage image,
                                      int rChan = 0, int gChan = 1, int bChan = 2)
 {
     return(InternalEncodeBC1(image, rChan, gChan, bChan, -1));
 }
Example #5
0
        /// <summary>
        /// Encodes an image into a block-compressed bitmap.
        /// </summary>
        /// <typeparam name="T">
        /// The block type to encode to.
        /// </typeparam>
        /// <param name="destImage">
        /// The bitmap to encode into. Encoding
        /// will overwrite its contents.
        /// </param>
        /// <param name="encoderFactory">
        /// A factory function to create encoder objects.
        /// Encoders must be thread-safe (see remarks).
        /// </param>
        /// <param name="srcImage">
        /// The image to encode.
        /// </param>
        /// <param name="channelIndices">
        /// The image channels to pass to the encoder. If this
        /// parmeter is <c>null</c>, all of the image's channels
        /// will be passed directly. A channel index of <c>-1</c>
        /// selects a <c>null</c> channel (this can be used to pad
        /// the array for encoders that expect their input data
        /// to be at specific channel indices).
        /// </param>
        /// <exception cref="ArgumentNullException">
        /// <paramref name="destImage"/>, <paramref name="encoderFactory"/>,
        /// or <paramref name="srcImage"/> is null.
        /// </exception>
        /// <exception cref="ArgumentException">
        /// <paramref name="destImage"/> and <paramref name="srcImage"/> are
        /// not the same size.
        /// </exception>
        /// <remarks>
        /// Blocks may be encoded in parallel. This function will call
        /// <paramref name="encoderFactory"/> multiple times, creating
        /// a unique encoder for each thread. Individual encoders will
        /// only run on one thread at a time, but multiple encoder objects
        /// may execute at once. Take care with any shared encoder state.
        /// </remarks>
        public static void EncodeBlocks <T>(
            BCnImage <T> destImage,
            Func <DataEncoder <T> > encoderFactory,
            FloatImage srcImage, int[] channelIndices = null)
            where T : struct
        {
            if (encoderFactory == null)
            {
                throw new ArgumentNullException("encoderFactory");
            }
            if (destImage == null)
            {
                throw new ArgumentNullException("destBitmap");
            }
            if (srcImage == null)
            {
                throw new ArgumentNullException("image");
            }

            if (destImage.Width != srcImage.Width || destImage.Height != srcImage.Height)
            {
                throw new ArgumentException("image and destBitmap must be the same size.");
            }

            var imageWidth  = srcImage.Width;
            var imageHeight = srcImage.Height;

            var blockWidth  = BCnImage <T> .BlockWidth;
            var blockHeight = BCnImage <T> .BlockHeight;

            var nBlocksW = destImage.WidthInBlocks;
            var nBlocksH = destImage.HeightInBlocks;

            var nWholeBlocksW = nBlocksW;
            var nWholeBlocksH = nBlocksH;

            if (imageWidth < nBlocksW * blockWidth)
            {
                nWholeBlocksW--;
            }
            if (imageHeight < nBlocksH * blockHeight)
            {
                nWholeBlocksH--;
            }

            float[][] chanData = srcImage.GetChannelStorage(channelIndices);

            var targetData = destImage.GetBlockStorage();

            Parallel.For(0, nWholeBlocksW * nWholeBlocksH,
                         encoderFactory, (iBlock, loopState, encoder) =>
            {
                int y = iBlock / nWholeBlocksW;
                int x = iBlock - y * nWholeBlocksW;

                int srcIndex =
                    (y * blockHeight) * imageWidth +
                    (x * blockWidth);

                targetData[y * nBlocksW + x] =
                    encoder(chanData, srcIndex, imageWidth);

                return(encoder);
            }, _ => { });

            //the uncommon case: finish off any partial edge tiles on the main thread

            if (nWholeBlocksW != nBlocksW || nWholeBlocksH != nBlocksH)
            {
                var blockArea = blockWidth * blockHeight;

                var tmp = new float[chanData.Length][];
                for (int iChan = 0; iChan < tmp.Length; iChan++)
                {
                    tmp[iChan] = chanData[iChan] != null ? new float[blockArea] : null;
                }

                var encoder = encoderFactory();

                if (nWholeBlocksH < nBlocksH)
                {
                    //get the row across the bottom

                    for (int xBlock = 0; xBlock < nBlocksW; xBlock++)
                    {
                        LoadEdgeBlockData(tmp, blockWidth, blockHeight,
                                          chanData, imageWidth, imageHeight,
                                          xBlock, nWholeBlocksH);
                        targetData[nWholeBlocksH * nBlocksW + xBlock] =
                            encoder(tmp, 0, blockWidth);
                    }

                    //munge this so the next pass doesn't recompress the corner block
                    nBlocksH--;
                }

                if (nWholeBlocksW < nBlocksW)
                {
                    //get the column at the right

                    for (int yBlock = 0; yBlock < nBlocksH; yBlock++)
                    {
                        LoadEdgeBlockData(tmp, blockWidth, blockHeight,
                                          chanData, imageWidth, imageHeight,
                                          nWholeBlocksW, yBlock);
                        targetData[yBlock * nBlocksW + nWholeBlocksW] =
                            encoder(tmp, 0, blockWidth);
                    }
                }
            }
        }
Example #6
0
        /// <summary>
        /// Encodes an image into the BC1 format.
        /// </summary>
        /// <param name="image">The image to encode.</param>
        /// <param name="rChan">The channel to pull red values from.</param>
        /// <param name="gChan">The channel to pull green values from.</param>
        /// <param name="bChan">The channel to pull blue values from.</param>
        /// <returns>The encoded BC1 image data.</returns>
        public BCnImage<BC1Block> EncodeBC1( FloatImage image,
			int rChan = 0, int gChan = 1, int bChan = 2 )
        {
            return InternalEncodeBC1( image, rChan, gChan, bChan, -1 );
        }
Example #7
0
        private BCnImage<BC1Block> InternalEncodeBC1( FloatImage image,
			int rChan, int gChan, int bChan, int aChan )
        {
            if( image == null )
                throw new ArgumentNullException( "image" );
            if( rChan < 0 || rChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "rChan" );
            if( gChan < 0 || gChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "gChan" );
            if( bChan < 0 || bChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "bChan" );
            if( aChan != -1 && (aChan < 0 || aChan >= image.ChannelCount) )
                throw new ArgumentOutOfRangeException( "aChan" );

            var ret = new BCnImage<BC1Block>( image.Width, image.Height );

            ImageEncodingHelper.EncodeBlocks( ret,
                () =>
                {
                    var encBC1 = new BC1BlockEncoder();

                    encBC1.DitherRgb = DitherRgb;
                    encBC1.DitherAlpha = DitherAlpha;

                    return ( chanData, index, pitch ) =>
                        {
                            encBC1.LoadBlock(
                                chanData[0], index,
                                chanData[1], index,
                                chanData[2], index,
                                pitch );

                            if( chanData[3] != null )
                                encBC1.LoadAlphaMask( chanData[3], index, 0.5F, pitch );

                            return encBC1.Encode();
                        };
                },
                image, new int[] { rChan, gChan, bChan, aChan } );

            return ret;
        }
Example #8
0
        /// <summary>
        /// Encodes an image into the unsigned BC5 format.
        /// </summary>
        /// <param name="image">The image to encode.</param>
        /// <param name="rChan">The channel to pull red values from.</param>
        /// <param name="gChan">The channel to pull green values from.</param>
        /// <returns>The encoded unsigned BC5 image data.</returns>
        public BCnImage<BC5UBlock> EncodeBC5U( FloatImage image,
			int rChan = 0, int gChan = 1 )
        {
            if( image == null )
                throw new ArgumentNullException( "image" );
            if( rChan < 0 || rChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "rChan" );
            if( gChan < 0 || gChan >= image.ChannelCount )
                throw new ArgumentOutOfRangeException( "gChan" );

            var ret = new BCnImage<BC5UBlock>( image.Width, image.Height );

            ImageEncodingHelper.EncodeBlocks( ret,
                () =>
                {
                    var encBC4 = new BC4BlockEncoder();

                    return ( chanData, index, pitch ) =>
                    {
                        BC5UBlock block;

                        encBC4.LoadBlock( chanData[0], index, pitch );
                        block.R = encBC4.EncodeUnsigned();

                        encBC4.LoadBlock( chanData[1], index, pitch );
                        block.G = encBC4.EncodeUnsigned();

                        return block;
                    };
                },
                image, new int[] { rChan, gChan } );

            return ret;
        }