protected override void Compress4(BlockWindow block) { // cache some values var count = m_colours.Count; var values = m_colours.Points; // create a codebook var codes = new Vec3[4]; codes[0] = m_start; codes[1] = m_end; codes[2] = (2.0f / 3.0f) * m_start + (1.0f / 3.0f) * m_end; codes[3] = (1.0f / 3.0f) * m_start + (2.0f / 3.0f) * m_end; // match each point to the closest code var closest = new Byte[16]; float error = 0.0f; for (int i = 0; i < count; ++i) { // find the closest code float dist = float.MaxValue; int idx = 0; for (int j = 0; j < 4; ++j) { float d = (m_metric * (values[i] - codes[j])).LengthSquared(); if (d < dist) { dist = d; idx = j; } } // save the index closest[i] = (Byte)idx; // accumulate the error error += dist; } // save this scheme if it wins if (error < m_besterror) { // remap the indices var indices = new Byte[16]; m_colours.RemapIndices(closest, indices); // save the block block.WriteColourBlock4(m_start, m_end, indices); // save the error m_besterror = error; } }
public void Compress(BlockWindow block) { if (block.IsDtx1) { if (m_colours.IsTransparent) { Compress3(block); } else { Compress4(block); } } else { Compress4(block); } }
protected override void Compress4(BlockWindow block) { // find the best end-points and index ComputeEndPoints(LOOKUPTABLES4); // build the block if we win if (m_error < m_besterror) { // remap the indices var indices = new Byte[16]; m_colours.RemapIndices(new Byte[] { m_index }, indices); // save the block block.WriteColourBlock4(m_start, m_end, indices); // save the error m_besterror = m_error; } }
private static void DecompressImage(Bitmap dstImage, Byte[] blocks, CompressionMode mode) { // initialise the block input var block = new BlockWindow(blocks, mode); var targetRgba = new Byte[4 * 16]; // loop over blocks for (int y = 0; y < dstImage.Height; y += 4) { for (int x = 0; x < dstImage.Width; x += 4) { // decompress the block block.Decompress(targetRgba); // write the decompressed pixels to the correct image locations dstImage.SetBlock(x, y, targetRgba); // advance block.Offset += block.ByteLength; } } }
protected abstract void Compress4(BlockWindow block);
protected override void Compress3(BlockWindow block) { // prepare an ordering using the principle axis ConstructOrdering(m_principle); // declare variables int count = m_colours.Count; Vec4 beststart = Vec4.Zero; Vec4 bestend = Vec4.Zero; var besterror = float.MaxValue; var bestbesterror = float.MaxValue; // check all possible clusters for this total order var indices = new Byte[16]; var bestindices = new Byte[16]; // first cluster [0,i) is at the start for (int m = 0; m < count; ++m) { indices[m] = 0; m_alpha[m] = m_weights[m]; m_beta[m] = 0; } for (int i = count; i >= 0; --i) { // second cluster [i,j) is half along for (int m = i; m < count; ++m) { indices[m] = 2; m_alpha[m] = m_beta[m] = 0.5f * m_weights[m]; } for (int j = count; j > i; --j) { // last cluster [j,k) is at the end if (j < count) { indices[j] = 1; m_alpha[j] = 0; m_beta[j] = m_weights[j]; } // solve a least squares problem to place the endpoints var error = SolveLeastSquares(out Vec4 start, out Vec4 end); // keep the solution if it wins if (error < besterror) { beststart = start; bestend = end; indices.CopyTo(bestindices, 0); besterror = error; } } } // save the block if necessary if (besterror < bestbesterror) { // remap the indices var unordered = new Byte[16]; for (int i = 0; i < count; ++i) { unordered[m_order[i]] = bestindices[i]; } m_colours.RemapIndices(unordered, bestindices); // save the block block.WriteColourBlock3(beststart.GetVec3(), bestend.GetVec3(), bestindices); // save the error bestbesterror = besterror; } }
private static void CompressImage(Bitmap srcImage, Byte[] blocks, CompressionMode mode, CompressionOptions options) { // fix any bad flags options = options.FixFlags(); int block_width = (srcImage.Width + 3) / 4; int block_height = (srcImage.Height + 3) / 4; // if the number of chunks to process is not very large, we better skip parallel processing if (block_width * block_height < 16) { options &= ~CompressionOptions.UseParallelProcessing; } if ((options & CompressionOptions.UseParallelProcessing) != 0) { System.Threading.Tasks.Parallel.For ( 0, block_height, (y, state) => { // initialise the block output var block = new BlockWindow(blocks, mode); block.Offset += block.ByteLength * y * block_width; // build the 4x4 block of pixels var sourceRgba = new Byte[16 * 4]; for (int x = 0; x < block_width; x++) { srcImage.CopyBlockTo(x * 4, y * 4, sourceRgba, out int mask); // compress it into the output block.CompressMasked(sourceRgba, mask, options); // advance block.Offset += block.ByteLength; } } ); } else { // initialise the block output var block = new BlockWindow(blocks, mode); // build the 4x4 block of pixels var sourceRgba = new Byte[16 * 4]; // loop over blocks for (int y = 0; y < block_height; ++y) { for (int x = 0; x < block_width; ++x) { srcImage.CopyBlockTo(x * 4, y * 4, sourceRgba, out int mask); // compress it into the output block.CompressMasked(sourceRgba, mask, options); // advance block.Offset += block.ByteLength; } } } }
protected override void Compress4(BlockWindow block) { // declare variables var count = m_colours.Count; // prepare an ordering using the principle axis ConstructOrdering(m_principle, 0); // check all possible clusters and iterate on the total order Vec4 beststart = Vec4.Zero; Vec4 bestend = Vec4.Zero; float m_besterror = float.MaxValue; float besterror = m_besterror; var bestindices = new Byte[16]; int bestiteration = 0; int besti = 0, bestj = 0, bestk = 0; // loop over iterations (we avoid the case that all points in first or last cluster) for (int iterationIndex = 0; ;) { // first cluster [0,i) is at the start Vec4 part0 = Vec4.Zero; for (int i = 0; i < count; ++i) { // second cluster [i,j) is one third along Vec4 part1 = Vec4.Zero; for (int j = i; ;) { // third cluster [j,k) is two thirds along Vec4 part2 = (j == 0) ? m_points_weights[0] : Vec4.Zero; int kmin = (j == 0) ? 1 : j; for (int k = kmin; ;) { // last cluster [k,count) is at the end Vec4 part3 = m_xsum_wsum - part2 - part1 - part0; // compute least squares terms directly Vec4 alphax_sum = ONETHIRD_ONETHIRD2.MultiplyAdd(part2, TWOTHIRDS_TWOTHIRDS2.MultiplyAdd(part1, part0)); Vec4 betax_sum = ONETHIRD_ONETHIRD2.MultiplyAdd(part1, TWOTHIRDS_TWOTHIRDS2.MultiplyAdd(part2, part3)); Vec4 alphabeta_sum = TWONINETHS * (part1 + part2).SplatW(); var error = ComputeLeastSquares(alphax_sum, betax_sum, alphabeta_sum, out Vec4 a, out Vec4 b); // keep the solution if it wins if (error < besterror) { beststart = a; bestend = b; besterror = error; besti = i; bestj = j; bestk = k; bestiteration = iterationIndex; } // advance if (k == count) { break; } part2 += m_points_weights[k]; ++k; } // advance if (j == count) { break; } part1 += m_points_weights[j]; ++j; } // advance part0 += m_points_weights[i]; } // stop if we didn't improve in this iteration if (bestiteration != iterationIndex) { break; } // advance if possible ++iterationIndex; if (iterationIndex == m_iterationCount) { break; } // stop if a new iteration is an ordering that has already been tried Vec3 axis = (bestend - beststart).GetVec3(); if (!ConstructOrdering(axis, iterationIndex)) { break; } } // save the block if necessary if (besterror < m_besterror) { // remap the indices var orderIndex = 16 * bestiteration; var unordered = new Byte[16]; for (int m = 0; m < besti; ++m) { unordered[m_order[orderIndex + m]] = 0; } for (int m = besti; m < bestj; ++m) { unordered[m_order[orderIndex + m]] = 2; } for (int m = bestj; m < bestk; ++m) { unordered[m_order[orderIndex + m]] = 3; } for (int m = bestk; m < count; ++m) { unordered[m_order[orderIndex + m]] = 1; } m_colours.RemapIndices(unordered, bestindices); // save the block block.WriteColourBlock4(beststart.GetVec3(), bestend.GetVec3(), bestindices); // save the error m_besterror = besterror; } }