/// <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 ); }
/// <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; }
/// <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); }
/// <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)); }
/// <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); } } } }
/// <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 ); }
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; }
/// <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; }