예제 #1
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);
        }
예제 #2
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);
                    }
                }
            }
        }
예제 #3
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));
 }