//**************************************************************//
        //********************  Color Block Luminance Distance  ********//
        //**************************************************************//

        protected static void findMinMaxColorsLuminanceDistance(ColorBlock4x4 block, Color32 minColor, Color32 maxColor)
        {
            int minLuminance = Integer.MAX_VALUE;
            int maxLuminance = -1;
            int minIndex     = 0;
            int maxIndex     = 0;

            for (int i = 0; i < 16; i++)
            {
                int luminance = colorLuminance(block.color[i]);
                if (luminance < minLuminance)
                {
                    minIndex     = i;
                    minLuminance = luminance;
                }
                if (luminance > maxLuminance)
                {
                    maxIndex     = i;
                    maxLuminance = luminance;
                }
            }

            copyColorComponents(block.color[minIndex], minColor);
            copyColorComponents(block.color[maxIndex], maxColor);
        }
Example #2
0
        /**
         * Compress the 4x4 color block into a DXT2/DXT3 block using 16 4 bit alpha values, and four colors. This method
         * compresses the color block exactly as a DXT1 compressor, except that it guarantees that the DXT1 block will use
         * four colors.
         * <p/>
         * Access to this method must be synchronized by the caller. This method is frequently invoked by the DXT
         * compressor, so in order to reduce garbage each instance of this class has unsynchronized properties that are
         * reused during each call.
         *
         * @param colorBlock the 4x4 color block to compress.
         * @param attributes attributes that will control the compression.
         * @param dxtBlock   the DXT2/DXT3 block that will receive the compressed data.
         * @throws ArgumentException if either <code>colorBlock</code> or <code>dxtBlock</code> are null.
         */
        public void compressBlockDXT3(ColorBlock4x4 colorBlock, DXTCompressionAttributes attributes, BlockDXT3 dxtBlock)
        {
            if (colorBlock == null)
            {
                String message = Logging.getMessage("nullValue.ColorBlockIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (attributes == null)
            {
                String message = Logging.getMessage("nullValue.AttributesIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (dxtBlock == null)
            {
                String message = Logging.getMessage("nullValue.DXTBlockIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            // The DXT3 color block is compressed exactly like the DXT1 color block, except that the four color palette is
            // always used, no matter the ordering of color0 and color1. At this stage we only consider color values,
            // not alpha.
            this.dxt1Compressor.compressBlockDXT1(colorBlock, attributes, dxtBlock.colorBlock);

            // The DXT3 alpha block can be compressed separately.
            this.compressBlockDXT3a(colorBlock, dxtBlock.alphaBlock);
        }
        public void compressImage(java.awt.image.BufferedImage image, DXTCompressionAttributes attributes,
                                  java.nio.ByteBuffer buffer)
        {
            if (image == null)
            {
                String message = Logging.getMessage("nullValue.ImageIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (attributes == null)
            {
                String message = Logging.getMessage("nullValue.AttributesIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (buffer == null)
            {
                String message = Logging.getMessage("nullValue.BufferNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            // If it is determined that the image and block have no alpha component, then we compress with DXT1 using a
            // four color palette. Otherwise, we use the three color palette (with the fourth color as transparent black).

            ColorBlock4x4       colorBlock          = new ColorBlock4x4();
            ColorBlockExtractor colorBlockExtractor = this.getColorBlockExtractor(image);

            BlockDXT1           dxt1Block      = new BlockDXT1();
            BlockDXT1Compressor dxt1Compressor = new BlockDXT1Compressor();

            int width  = image.getWidth();
            int height = image.getHeight();

            bool imageHasAlpha  = image.getColorModel().hasAlpha();
            bool enableAlpha    = attributes.isEnableDXT1Alpha();
            int  alphaThreshold = attributes.getDXT1AlphaThreshold();

            for (int j = 0; j < height; j += 4)
            {
                for (int i = 0; i < width; i += 4)
                {
                    colorBlockExtractor.extractColorBlock4x4(attributes, i, j, colorBlock);

                    if (enableAlpha && imageHasAlpha && blockHasDXT1Alpha(colorBlock, alphaThreshold))
                    {
                        dxt1Compressor.compressBlockDXT1a(colorBlock, attributes, dxt1Block);
                    }
                    else
                    {
                        dxt1Compressor.compressBlockDXT1(colorBlock, attributes, dxt1Block);
                    }

                    buffer.putShort((short)dxt1Block.color0);
                    buffer.putShort((short)dxt1Block.color1);
                    buffer.putInt((int)dxt1Block.colorIndexMask);
                }
            }
        }
        //**************************************************************//
        //********************  Color Block Box Fitting  ***************//
        //**************************************************************//

        protected static void findMinMaxColorsBox(ColorBlock4x4 block, Color32 minColor, Color32 maxColor)
        {
            minColor.r = minColor.g = minColor.b = 255;
            maxColor.r = maxColor.g = maxColor.b = 0;

            for (int i = 0; i < 16; i++)
            {
                minColorComponents(minColor, block.color[i], minColor);
                maxColorComponents(maxColor, block.color[i], maxColor);
            }
        }
        protected bool blockHasDXT1Alpha(ColorBlock4x4 colorBlock, int alphaThreshold)
        {
            // DXT1 provides support for binary alpha. Therefore we determine treat a color block as needing alpha support
            // if any of the alpha values are less than a certain threshold.

            for (int i = 0; i < 16; i++)
            {
                if (colorBlock.color[i].a < alphaThreshold)
                {
                    return(true);
                }
            }

            return(false);
        }
Example #6
0
        public void compressImage(java.awt.image.BufferedImage image, DXTCompressionAttributes attributes,
                                  java.nio.ByteBuffer buffer)
        {
            if (image == null)
            {
                String message = Logging.getMessage("nullValue.ImageIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (attributes == null)
            {
                String message = Logging.getMessage("nullValue.AttributesIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (buffer == null)
            {
                String message = Logging.getMessage("nullValue.BufferNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            ColorBlock4x4       colorBlock          = new ColorBlock4x4();
            ColorBlockExtractor colorBlockExtractor = this.getColorBlockExtractor(image);

            BlockDXT3           dxt3Block      = new BlockDXT3();
            BlockDXT3Compressor dxt3Compressor = new BlockDXT3Compressor();

            int width  = image.getWidth();
            int height = image.getHeight();

            for (int j = 0; j < height; j += 4)
            {
                for (int i = 0; i < width; i += 4)
                {
                    colorBlockExtractor.extractColorBlock4x4(attributes, i, j, colorBlock);
                    dxt3Compressor.compressBlockDXT3(colorBlock, attributes, dxt3Block);

                    AlphaBlockDXT3 dxtAlphaBlock = dxt3Block.getAlphaBlock();
                    buffer.putLong(dxtAlphaBlock.alphaValueMask);

                    BlockDXT1 dxtColorBlock = dxt3Block.getColorBlock();
                    buffer.putShort((short)dxtColorBlock.color0);
                    buffer.putShort((short)dxtColorBlock.color1);
                    buffer.putInt((int)dxtColorBlock.colorIndexMask);
                }
            }
        }
        protected static long computePaletteIndices3(ColorBlock4x4 block, DXTCompressionAttributes attributes,
                                                     Color32[] palette)
        {
            // This implementation is based on code available in the nvidia-texture-tools project:
            // http://code.google.com/p/nvidia-texture-tools/
            //
            // If the pixel alpha is below the specified threshold, we return index 3. In a three color DXT1 palette,
            // index 3 is interpreted as transparent black. Otherwise, we compare the sums of absolute differences, and
            // choose the nearest color index.

            int alphaThreshold = attributes.getDXT1AlphaThreshold();

            long mask = 0L;
            long index;

            for (int i = 0; i < 16; i++)
            {
                int d0 = colorDistanceSquared(palette[0], block.color[i]);
                int d1 = colorDistanceSquared(palette[1], block.color[i]);
                int d2 = colorDistanceSquared(palette[2], block.color[i]);

                // TODO: implement bit twiddle as in computePaletteIndex4 to avoid conditional branching

                if (block.color[i].a < alphaThreshold)
                {
                    index = 3;
                }
                else if (d0 < d1 && d0 < d2)
                {
                    index = 0;
                }
                else if (d1 < d2)
                {
                    index = 1;
                }
                else
                {
                    index = 2;
                }

                mask |= (index << (i << 1));
            }

            return(mask);
        }
        protected static void selectDiagonal(ColorBlock4x4 block, Color32 minColor, Color32 maxColor)
        {
            int centerR = (minColor.r + maxColor.r) / 2;
            int centerG = (minColor.g + maxColor.g) / 2;
            int centerB = (minColor.b + maxColor.b) / 2;

            int cvx = 0;
            int cvy = 0;

            for (int i = 0; i < 16; i++)
            {
                int tx = block.color[i].r - centerR;
                int ty = block.color[i].g - centerG;
                int tz = block.color[i].b - centerB;

                cvx += tx * tz;
                cvy += ty * tz;
            }

            int x0 = minColor.r;
            int y0 = minColor.g;
            int x1 = maxColor.r;
            int y1 = maxColor.g;

            if (cvx < 0)
            {
                int tmp = x0;
                x0 = x1;
                x1 = tmp;
            }

            if (cvy < 0)
            {
                int tmp = y0;
                y0 = y1;
                y1 = tmp;
            }

            minColor.r = x0;
            minColor.g = y0;
            maxColor.r = x1;
            maxColor.g = y1;
        }
        /**
         * Compress the 4x4 color block into a DXT1 block using four colors. This method ignores transparency and
         * guarantees that the DXT1 block will use four colors.
         * <p>
         * Access to this method must be synchronized by the caller. This method is frequently invoked by the DXT
         * compressor, so in order to reduce garbage each instance of this class has unsynchronized properties that are
         * reused during each call.
         *
         * @param colorBlock the 4x4 color block to compress.
         * @param attributes attributes that will control the compression.
         * @param dxtBlock the DXT1 block that will receive the compressed data.
         *
         * @throws ArgumentException if either <code>colorBlock</code> or <code>dxtBlock</code> are null.
         */
        public void compressBlockDXT1(ColorBlock4x4 colorBlock, DXTCompressionAttributes attributes, BlockDXT1 dxtBlock)
        {
            if (colorBlock == null)
            {
                String message = Logging.getMessage("nullValue.ColorBlockIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (attributes == null)
            {
                String message = Logging.getMessage("nullValue.AttributesIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (dxtBlock == null)
            {
                String message = Logging.getMessage("nullValue.DXTBlockIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            this.chooseMinMaxColors(colorBlock, attributes, this.minColor, this.maxColor);
            int color0 = short565FromColor32(this.maxColor);
            int color1 = short565FromColor32(this.minColor);

            if (color0 < color1)
            {
                int tmp = color0;
                color0 = color1;
                color1 = tmp;
            }

            // To get a four color palette with no alpha, the first color must be greater than the second color.
            computeColorPalette4(color0, color1, this.palette);

            dxtBlock.color0         = color0;
            dxtBlock.color1         = color1;
            dxtBlock.colorIndexMask = computePaletteIndices4(colorBlock, this.palette);
        }
 protected void chooseMinMaxColors(ColorBlock4x4 block, DXTCompressionAttributes attributes,
                                   Color32 minColor, Color32 maxColor)
 {
     //noinspection StringEquality
     if (attributes.getColorBlockCompressionType() == DXTCompressionAttributes.COLOR_BLOCK_COMPRESSION_BBOX)
     {
         findMinMaxColorsBox(block, minColor, maxColor);
         selectDiagonal(block, minColor, maxColor);
         insetBox(minColor, maxColor);
     }
     else //noinspection StringEquality
     if (attributes.getColorBlockCompressionType() == DXTCompressionAttributes.COLOR_BLOCK_COMPRESSION_EUCLIDEAN_DISTANCE)
     {
         findMinMaxColorsEuclideanDistance(block, minColor, maxColor);
     }
     else //noinspection StringEquality
     if (attributes.getColorBlockCompressionType() == DXTCompressionAttributes.COLOR_BLOCK_COMPRESSION_LUMINANCE_DISTANCE)
     {
         // Default to using euclidean distance to compute the min and max palette colors.
         findMinMaxColorsLuminanceDistance(block, minColor, maxColor);
     }
 }
        //**************************************************************//
        //********************  Color Block Euclidean Distance  ********//
        //**************************************************************//

        protected static void findMinMaxColorsEuclideanDistance(ColorBlock4x4 block, Color32 minColor, Color32 maxColor)
        {
            double maxDistance = -1.0;
            int    minIndex    = 0;
            int    maxIndex    = 0;

            for (int i = 0; i < 15; i++)
            {
                for (int j = i + 1; j < 16; j++)
                {
                    double d = colorDistanceSquared(block.color[i], block.color[j]);
                    if (d > maxDistance)
                    {
                        minIndex    = i;
                        maxIndex    = j;
                        maxDistance = d;
                    }
                }
            }

            copyColorComponents(block.color[minIndex], minColor);
            copyColorComponents(block.color[maxIndex], maxColor);
        }
Example #12
0
        //**************************************************************//
        //********************  Alpha Block Assembly  ******************//
        //**************************************************************//

        protected static long computeAlphaValueMask(ColorBlock4x4 colorBlock)
        {
            // Alpha is encoded as 4 bit values. Each pair of values will be packed into one byte. The first value goes
            // in bits 0-4, and the second value goes in bits 5-8. The resultant 64 bit value is structured so that when
            // converted to little endian ordering, the alpha values will be in the correct order. Here's what the
            // structure looks like packed into Java's long, where the value aN represents the Nth alpha value in
            // hexadecimal notation.
            //
            //  | 63-56 | 55-48 | 47-40 | 39-32 | 31-24 | 23-16 | 15-8  | 7-0   |
            //  | aFaE  | aDaC  | aBaA  | a9a8  | a7a6  | a5a4  | a3a2  | a1a0  |

            long bitmask = 0L;

            for (int i = 0; i < 8; i++)
            {
                int  a0     = 0xF & alpha4FromAlpha8(colorBlock.color[2 * i].a);
                int  a1     = 0xF & alpha4FromAlpha8(colorBlock.color[2 * i + 1].a);
                long mask10 = (a1 << 4) | a0;
                bitmask |= (mask10 << (8 * i));
            }

            return(bitmask);
        }
        protected static long computePaletteIndices4(ColorBlock4x4 block, Color32[] palette)
        {
            // This implementation is based on the paper by J.M.P. van Waveren:
            // http://cache-www.intel.com/cd/00/00/32/43/324337_324337.pdf
            //
            // We compare the sums of absolute differences, and choose the nearest color index. We avoid conditional
            // branching to determine the nearest index, which would suffer from a lot of branch mispredition. Instead,
            // we compute each distance and derive a 2-bit binary index directly from the results of the distance
            // comparisons.

            long mask = 0L;
            long index;

            for (int i = 0; i < 16; i++)
            {
                int d0 = colorDistanceSquared(palette[0], block.color[i]);
                int d1 = colorDistanceSquared(palette[1], block.color[i]);
                int d2 = colorDistanceSquared(palette[2], block.color[i]);
                int d3 = colorDistanceSquared(palette[3], block.color[i]);

                int b0 = greaterThan(d0, d3);
                int b1 = greaterThan(d1, d2);
                int b2 = greaterThan(d0, d2);
                int b3 = greaterThan(d1, d3);
                int b4 = greaterThan(d2, d3);

                int x0 = b1 & b2;
                int x1 = b0 & b3;
                int x2 = b0 & b4;

                index = (x2 | ((x0 | x1) << 1));

                mask |= (index << (i << 1));
            }

            return(mask);
        }
        /**
         * Extracts a 4x4 block of pixel data at the specified coordinate <code>(x, y)</code>, and places the data in the
         * specified <code>colorBlock</code>. If the coordinate <code>(x, y)</code> with the image, but the entire 4x4
         * block is not, this will either truncate the block to fit the image, or copy nearby pixels to fill the block. If
         * the <code>attributes</code> specify that color components should be premultiplied by alpha, this extactor will
         * perform the premultiplication operation on the incoming colors.
         * <p>
         * Access to this method must be synchronized by the caller. This method is frequenty invoked by the DXT
         * compressor, so in order to reduce garbage each instance of this class has unsynchronized properties that are
         * reused during each call.
         *
         * @param attributes the DXT compression attributes which may affect how colors are accessed.
         * @param x horizontal coordinate origin to extract pixel data from.
         * @param y vertical coordainte origin to extract pixel data from.
         * @param colorBlock 4x4 block of pixel data that will receive the data.
         *
         * @throws ArgumentException if either <code>attributes</code> or <code>colorBlock</code> is null.
         */
        public void extractColorBlock4x4(DXTCompressionAttributes attributes, int x, int y, ColorBlock4x4 colorBlock)
        {
            if (attributes == null)
            {
                String message = Logging.getMessage("nullValue.AttributesIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }
            if (colorBlock == null)
            {
                String message = Logging.getMessage("nullValue.ColorBlockIsNull");
                Logging.logger().severe(message);
                throw new ArgumentException(message);
            }

            // Image blocks that are smaller than 4x4 are handled by repeating the image pixels that intersect the
            // requested block range.

            int bw = Math.Min(this.width - x, 4);
            int bh = Math.Min(this.height - y, 4);
            int bxOffset = 4 * (bw - 1);
            int byOffset = 4 * (bh - 1);
            int bx, by;
            int blockPos = 0;

            // Extracts color data from the image in INT_ARGB format. So each integer in the buffer is a tightly packed
            // 8888 ARGB int, where the color components are not considered to be premultiplied.
            this.image.getRGB(x, y, bw, bh, this.buffer, 0, 4);

            for (int j = 0; j < 4; j++)
            {
                by = remainder[byOffset + j];

                bx = remainder[bxOffset];
                int32ToColor32(this.buffer[bx + by * 4], colorBlock.color[blockPos++]);

                bx = remainder[bxOffset + 1];
                int32ToColor32(this.buffer[bx + by * 4], colorBlock.color[blockPos++]);

                bx = remainder[bxOffset + 2];
                int32ToColor32(this.buffer[bx + by * 4], colorBlock.color[blockPos++]);

                bx = remainder[bxOffset + 3];
                int32ToColor32(this.buffer[bx + by * 4], colorBlock.color[blockPos++]);
            }

            if (attributes.isPremultiplyAlpha())
            {
                for (int i = 0; i < 16; i++)
                {
                    premultiplyAlpha(colorBlock.color[i]);
                }
            }
        }
Example #15
0
 protected void compressBlockDXT3a(ColorBlock4x4 colorBlock, AlphaBlockDXT3 dxtBlock)
 {
     dxtBlock.alphaValueMask = computeAlphaValueMask(colorBlock);
 }