[Buildroot] [PATCH/RFC v2 01/44] package/libsquish: New package
Bernd Kuhls
bernd.kuhls at t-online.de
Sun Jun 14 14:36:00 UTC 2015
Needed for Kodi 15.0
Signed-off-by: Bernd Kuhls <bernd.kuhls at t-online.de>
---
package/Config.in | 1 +
package/libsquish/0001-kodi.patch | 1504 +++++++++++++++++++++++++++++++++++++
package/libsquish/Config.in | 11 +
package/libsquish/libsquish.hash | 2 +
package/libsquish/libsquish.mk | 27 +
5 files changed, 1545 insertions(+)
create mode 100644 package/libsquish/0001-kodi.patch
create mode 100644 package/libsquish/Config.in
create mode 100644 package/libsquish/libsquish.hash
create mode 100644 package/libsquish/libsquish.mk
diff --git a/package/Config.in b/package/Config.in
index 6dbc32d..8823c8d 100644
--- a/package/Config.in
+++ b/package/Config.in
@@ -705,6 +705,7 @@ endmenu
menu "Compression and decompression"
source "package/libarchive/Config.in"
source "package/libzip/Config.in"
+ source "package/libsquish/Config.in"
source "package/lzo/Config.in"
source "package/snappy/Config.in"
source "package/szip/Config.in"
diff --git a/package/libsquish/0001-kodi.patch b/package/libsquish/0001-kodi.patch
new file mode 100644
index 0000000..1bab6a6
--- /dev/null
+++ b/package/libsquish/0001-kodi.patch
@@ -0,0 +1,1504 @@
+Add Kodi-specific patch
+
+Kodi 15.0 contains an updated version of libsquish:
+https://github.com/xbmc/xbmc/tree/master/tools/depends/native/libsquish-native
+
+The OpenElec project provides a separate tarball including the Kodi-
+specific patches:
+http://sources.openelec.tv/devel/libsquish-1.10-openelec.tar.gz
+
+This patch is the diff between upstream libsquish 1.10 and the OpenElec
+tarball without the vs7/ directory.
+
+Signed-off-by: Bernd Kuhls <bernd.kuhls at t-online.de>
+
+diff -uwNr squish-1.10/alpha.cpp libsquish-1.10-openelec/alpha.cpp
+--- squish-1.10/alpha.cpp 2006-06-29 14:43:24.000000000 +0200
++++ libsquish-1.10-openelec/alpha.cpp 2015-01-09 10:58:43.000000000 +0100
+@@ -24,6 +24,7 @@
+ -------------------------------------------------------------------------- */
+
+ #include "alpha.h"
++#include <limits.h>
+ #include <algorithm>
+
+ namespace squish {
+diff -uwNr squish-1.10/clusterfit.cpp libsquish-1.10-openelec/clusterfit.cpp
+--- squish-1.10/clusterfit.cpp 2007-03-23 11:19:20.000000000 +0100
++++ libsquish-1.10-openelec/clusterfit.cpp 2015-01-09 10:58:43.000000000 +0100
+@@ -31,22 +31,21 @@
+
+ namespace squish {
+
+-ClusterFit::ClusterFit( ColourSet const* colours, int flags )
++ClusterFit::ClusterFit( ColourSet const* colours, int flags, float* metric )
+ : ColourFit( colours, flags )
+ {
+ // set the iteration count
+ m_iterationCount = ( m_flags & kColourIterativeClusterFit ) ? kMaxIterations : 1;
+
+- // initialise the best error
+- m_besterror = VEC4_CONST( FLT_MAX );
+-
+- // initialise the metric
+- bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 );
+- if( perceptual )
+- m_metric = Vec4( 0.2126f, 0.7152f, 0.0722f, 0.0f );
++ // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
++ if( metric )
++ m_metric = Vec4( metric[0], metric[1], metric[2], 1.0f );
+ else
+ m_metric = VEC4_CONST( 1.0f );
+
++ // initialise the best error
++ m_besterror = VEC4_CONST( FLT_MAX );
++
+ // cache some values
+ int const count = m_colours->GetCount();
+ Vec3 const* values = m_colours->GetPoints();
+diff -uwNr squish-1.10/clusterfit.h libsquish-1.10-openelec/clusterfit.h
+--- squish-1.10/clusterfit.h 2007-03-23 11:19:20.000000000 +0100
++++ libsquish-1.10-openelec/clusterfit.h 2015-01-09 10:58:43.000000000 +0100
+@@ -37,7 +37,7 @@
+ class ClusterFit : public ColourFit
+ {
+ public:
+- ClusterFit( ColourSet const* colours, int flags );
++ ClusterFit( ColourSet const* colours, int flags, float* metric );
+
+ private:
+ bool ConstructOrdering( Vec3 const& axis, int iteration );
+diff -uwNr squish-1.10/colourfit.cpp libsquish-1.10-openelec/colourfit.cpp
+--- squish-1.10/colourfit.cpp 2006-04-07 19:30:11.000000000 +0200
++++ libsquish-1.10-openelec/colourfit.cpp 2015-01-09 10:58:43.000000000 +0100
+@@ -34,6 +34,10 @@
+ {
+ }
+
++ColourFit::~ColourFit()
++{
++}
++
+ void ColourFit::Compress( void* block )
+ {
+ bool isDxt1 = ( ( m_flags & kDxt1 ) != 0 );
+diff -uwNr squish-1.10/colourfit.h libsquish-1.10-openelec/colourfit.h
+--- squish-1.10/colourfit.h 2006-04-07 19:30:11.000000000 +0200
++++ libsquish-1.10-openelec/colourfit.h 2015-01-09 10:58:43.000000000 +0100
+@@ -27,6 +27,7 @@
+ #define SQUISH_COLOURFIT_H
+
+ #include <squish.h>
++#include <limits.h>
+ #include "maths.h"
+
+ namespace squish {
+@@ -37,6 +38,7 @@
+ {
+ public:
+ ColourFit( ColourSet const* colours, int flags );
++ virtual ~ColourFit();
+
+ void Compress( void* block );
+
+diff -uwNr squish-1.10/config libsquish-1.10-openelec/config
+--- squish-1.10/config 2006-10-01 21:40:09.000000000 +0200
++++ libsquish-1.10-openelec/config 2015-01-09 16:51:36.000000000 +0100
+@@ -18,5 +18,4 @@
+ endif
+
+ # where should we install to
+-INSTALL_DIR ?= /usr/local
+-
++INSTALL_DIR ?= @PREFIX@
+diff -uwNr squish-1.10/config.h libsquish-1.10-openelec/config.h
+--- squish-1.10/config.h 2006-10-01 21:40:09.000000000 +0200
++++ libsquish-1.10-openelec/config.h 2015-01-09 10:58:43.000000000 +0100
+@@ -28,15 +28,25 @@
+
+ // Set to 1 when building squish to use Altivec instructions.
+ #ifndef SQUISH_USE_ALTIVEC
++#if defined(__ALTIVEC__)
++#define SQUISH_USE_ALTIVEC 1
++#else
+ #define SQUISH_USE_ALTIVEC 0
+ #endif
++#endif
+
+ // Set to 1 or 2 when building squish to use SSE or SSE2 instructions.
+ #ifndef SQUISH_USE_SSE
++#if defined(__SSE2__)
++#define SQUISH_USE_SSE 2
++#elif defined(__SSE__)
++#define SQUISH_USE_SSE 1
++#else
+ #define SQUISH_USE_SSE 0
+ #endif
++#endif
+
+-// Internally et SQUISH_USE_SIMD when either Altivec or SSE is available.
++// Internally set SQUISH_USE_SIMD when either Altivec or SSE is available.
+ #if SQUISH_USE_ALTIVEC && SQUISH_USE_SSE
+ #error "Cannot enable both Altivec and SSE!"
+ #endif
+diff -uwNr squish-1.10/config.in libsquish-1.10-openelec/config.in
+--- squish-1.10/config.in 1970-01-01 01:00:00.000000000 +0100
++++ libsquish-1.10-openelec/config.in 2015-01-09 10:58:43.000000000 +0100
+@@ -0,0 +1,21 @@
++# config file used for the Makefile only
++
++# define to 1 to use Altivec instructions
++USE_ALTIVEC ?= 0
++
++# define to 1 to use SSE2 instructions
++USE_SSE ?= 0
++
++# default flags
++CXXFLAGS ?= -O2
++ifeq ($(USE_ALTIVEC),1)
++CPPFLAGS += -DSQUISH_USE_ALTIVEC=1
++CXXFLAGS += -maltivec
++endif
++ifeq ($(USE_SSE),1)
++CPPFLAGS += -DSQUISH_USE_SSE=2
++CXXFLAGS += -msse
++endif
++
++# where should we install to
++INSTALL_DIR ?= @PREFIX@
+diff -uwNr squish-1.10/extra/squishpng.cpp libsquish-1.10-openelec/extra/squishpng.cpp
+--- squish-1.10/extra/squishpng.cpp 2007-03-21 20:31:51.000000000 +0100
++++ libsquish-1.10-openelec/extra/squishpng.cpp 2015-01-09 10:58:43.000000000 +0100
+@@ -25,11 +25,13 @@
+
+ /*! @file
+
+- @brief Example program that converts between the PNG and DXT formats.
++ @brief Test program that compresses images loaded using the PNG format.
+
+- This program requires libpng for PNG input and output, and is designed
+- to show how to prepare data for the squish library when it is not simply
+- a contiguous block of memory.
++ This program requires libpng for PNG input and output, and is designed to
++ test the RMS error for DXT compression for a set of test images.
++
++ This program uses the high-level image compression and decompression
++ functions that process an entire image at a time.
+ */
+
+ #include <iostream>
+@@ -74,9 +76,17 @@
+ class Mem : NonCopyable
+ {
+ public:
++ Mem() : m_p( 0 ) {}
+ explicit Mem( int size ) : m_p( new u8[size] ) {}
+ ~Mem() { delete[] m_p; }
+
++ void Reset( int size )
++ {
++ u8 *p = new u8[size];
++ delete m_p;
++ m_p = p;
++ }
++
+ u8* Get() const { return m_p; }
+
+ private:
+@@ -172,53 +182,71 @@
+ class PngRows : NonCopyable
+ {
+ public:
+- PngRows( int width, int height, int stride ) : m_width( width ), m_height( height )
++ PngRows( int pitch, int height ) : m_height( height )
+ {
+- m_rows = ( png_bytep* )malloc( m_height*sizeof( png_bytep ) );
++ m_rows = new png_bytep[m_height];
+ for( int i = 0; i < m_height; ++i )
+- m_rows[i] = ( png_bytep )malloc( m_width*stride );
++ m_rows[i] = new png_byte[pitch];
+ }
+
+ ~PngRows()
+ {
+ for( int i = 0; i < m_height; ++i )
+- free( m_rows[i] );
+- free( m_rows );
++ delete[] m_rows[i];
++ delete[] m_rows;
+ }
+
+ png_bytep* Get() const { return m_rows; }
+
++ png_bytep operator[](int y) const { return m_rows[y]; }
++
+ private:
+ png_bytep* m_rows;
+- int m_width, m_height;
++ int m_height;
+ };
+
+-class PngImage
++//! Represents a DXT compressed image in memory.
++struct DxtData
++{
++ int width;
++ int height;
++ int format; //!< Either kDxt1, kDxt3 or kDxt5.
++ Mem data;
++ bool isColour;
++ bool isAlpha;
++};
++
++//! Represents an uncompressed RGBA image in memory.
++class Image
+ {
+ public:
+- explicit PngImage( std::string const& fileName );
++ Image();
+
+- int GetWidth() const { return m_width; }
+- int GetHeight() const { return m_height; }
+- int GetStride() const { return m_stride; }
+- bool IsColour() const { return m_colour; }
+- bool IsAlpha() const { return m_alpha; }
++ void LoadPng( std::string const& fileName );
++ void SavePng( std::string const& fileName ) const;
+
+- u8 const* GetRow( int row ) const { return ( u8* )m_rows[row]; }
++ void Decompress( DxtData const& dxt );
++ void Compress( DxtData& dxt, int flags ) const;
+
+-private:
+- PngReadStruct m_png;
++ double GetRmsError( Image const& image ) const;
+
++private:
+ int m_width;
+ int m_height;
+- int m_stride;
+- bool m_colour;
+- bool m_alpha;
+-
+- png_bytep* m_rows;
++ bool m_isColour; //!< Either colour or luminance.
++ bool m_isAlpha; //!< Either alpha or not.
++ Mem m_pixels;
+ };
+
+-PngImage::PngImage( std::string const& fileName )
++Image::Image()
++ : m_width( 0 ),
++ m_height( 0 ),
++ m_isColour( false ),
++ m_isAlpha( false )
++{
++}
++
++void Image::LoadPng( std::string const& fileName )
+ {
+ // open the source file
+ File file( fopen( fileName.c_str(), "rb" ) );
+@@ -231,7 +259,9 @@
+
+ // check the signature bytes
+ png_byte header[8];
+- fread( header, 1, 8, file.Get() );
++ size_t check = fread( header, 1, 8, file.Get() );
++ if( check != 8 )
++ throw Error( "file read error" );
+ if( png_sig_cmp( header, 0, 8 ) )
+ {
+ std::ostringstream oss;
+@@ -240,16 +270,17 @@
+ }
+
+ // read the image into memory
+- png_init_io( m_png.GetPng(), file.Get() );
+- png_set_sig_bytes( m_png.GetPng(), 8 );
+- png_read_png( m_png.GetPng(), m_png.GetInfo(), PNG_TRANSFORM_EXPAND, 0 );
++ PngReadStruct png;
++ png_init_io( png.GetPng(), file.Get() );
++ png_set_sig_bytes( png.GetPng(), 8 );
++ png_read_png( png.GetPng(), png.GetInfo(), PNG_TRANSFORM_EXPAND, 0 );
+
+ // get the image info
+ png_uint_32 width;
+ png_uint_32 height;
+ int bitDepth;
+ int colourType;
+- png_get_IHDR( m_png.GetPng(), m_png.GetInfo(), &width, &height, &bitDepth, &colourType, 0, 0, 0 );
++ png_get_IHDR( png.GetPng(), png.GetInfo(), &width, &height, &bitDepth, &colourType, 0, 0, 0 );
+
+ // check the image is 8 bit
+ if( bitDepth != 8 )
+@@ -259,234 +290,156 @@
+ throw Error( oss.str() );
+ }
+
+- // save the info
++ // copy the data into a contiguous array
+ m_width = width;
+ m_height = height;
+- m_colour = ( ( colourType & PNG_COLOR_MASK_COLOR ) != 0 );
+- m_alpha = ( ( colourType & PNG_COLOR_MASK_ALPHA ) != 0 );
+- m_stride = ( m_colour ? 3 : 1 ) + ( m_alpha ? 1 : 0 );
++ m_isColour = ( ( colourType & PNG_COLOR_MASK_COLOR ) != 0 );
++ m_isAlpha = ( ( colourType & PNG_COLOR_MASK_ALPHA ) != 0 );
++ m_pixels.Reset(4*width*height);
+
+ // get the image rows
+- m_rows = png_get_rows( m_png.GetPng(), m_png.GetInfo() );
+- if( !m_rows )
++ png_bytep const *rows = png_get_rows( png.GetPng(), png.GetInfo() );
++ if( !rows )
+ throw Error( "failed to get image rows" );
+-}
+-
+-static void Compress( std::string const& sourceFileName, std::string const& targetFileName, int flags )
+-{
+- // load the source image
+- PngImage sourceImage( sourceFileName );
+-
+- // get the image info
+- int width = sourceImage.GetWidth();
+- int height = sourceImage.GetHeight();
+- int stride = sourceImage.GetStride();
+- bool colour = sourceImage.IsColour();
+- bool alpha = sourceImage.IsAlpha();
+
+- // check the image dimensions
+- if( ( width % 4 ) != 0 || ( height % 4 ) != 0 )
+- {
+- std::ostringstream oss;
+- oss << "cannot compress " << width << "x" << height
+- << "image (dimensions must be multiples of 4)";
+- throw Error( oss.str() );
+- }
+-
+- // create the target data
+- int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
+- int targetDataSize = bytesPerBlock*width*height/16;
+- Mem targetData( targetDataSize );
+-
+- // loop over blocks and compress them
+- clock_t start = std::clock();
+- u8* targetBlock = targetData.Get();
+- for( int y = 0; y < height; y += 4 )
+- {
+- // process a row of blocks
+- for( int x = 0; x < width; x += 4 )
+- {
+- // get the block data
+- u8 sourceRgba[16*4];
+- for( int py = 0, i = 0; py < 4; ++py )
+- {
+- u8 const* row = sourceImage.GetRow( y + py ) + x*stride;
+- for( int px = 0; px < 4; ++px, ++i )
+- {
+- // get the pixel colour
+- if( colour )
+- {
+- for( int j = 0; j < 3; ++j )
+- sourceRgba[4*i + j] = *row++;
++ // copy the pixels into the storage
++ u8 *dest = m_pixels.Get();
++ for( int y = 0; y < m_height; ++y )
++ {
++ u8 const *src = rows[y];
++ for( int x = 0; x < m_width; ++x )
++ {
++ if( m_isColour )
++ {
++ dest[0] = src[0];
++ dest[1] = src[1];
++ dest[2] = src[2];
++ src += 3;
+ }
+ else
+ {
+- for( int j = 0; j < 3; ++j )
+- sourceRgba[4*i + j] = *row;
+- ++row;
++ u8 lum = *src++;
++ dest[0] = lum;
++ dest[1] = lum;
++ dest[2] = lum;
+ }
+
+- // skip alpha for now
+- if( alpha )
+- sourceRgba[4*i + 3] = *row++;
++ if( m_isAlpha )
++ dest[3] = *src++;
+ else
+- sourceRgba[4*i + 3] = 255;
+- }
+- }
+-
+- // compress this block
+- Compress( sourceRgba, targetBlock, flags );
++ dest[3] = 255;
+
+- // advance
+- targetBlock += bytesPerBlock;
++ dest += 4;
+ }
+ }
+- clock_t end = std::clock();
+- double duration = ( double )( end - start ) / CLOCKS_PER_SEC;
+- std::cout << "time taken: " << duration << " seconds" << std::endl;
+-
+- // open the target file
+- File targetFile( fopen( targetFileName.c_str(), "wb" ) );
+- if( !targetFile.IsValid() )
+- {
+- std::ostringstream oss;
+- oss << "failed to open \"" << sourceFileName << "\" for writing";
+- throw Error( oss.str() );
+ }
+
+- // write the header
+- fwrite( &width, sizeof( int ), 1, targetFile.Get() );
+- fwrite( &height, sizeof( int ), 1, targetFile.Get() );
+-
+- // write the data
+- fwrite( targetData.Get(), 1, targetDataSize, targetFile.Get() );
+-}
+-
+-static void Decompress( std::string const& sourceFileName, std::string const& targetFileName, int flags )
+-{
+- // open the source file
+- File sourceFile( fopen( sourceFileName.c_str(), "rb" ) );
+- if( !sourceFile.IsValid() )
++void Image::SavePng( std::string const& fileName ) const
+ {
+- std::ostringstream oss;
+- oss << "failed to open \"" << sourceFileName << "\" for reading";
+- throw Error( oss.str() );
+- }
+-
+- // get the width and height
+- int width, height;
+- fread( &width, sizeof( int ), 1, sourceFile.Get() );
+- fread( &height, sizeof( int ), 1, sourceFile.Get() );
+-
+- // work out the data size
+- int bytesPerBlock = ( ( flags & kDxt1 ) != 0 ) ? 8 : 16;
+- int sourceDataSize = bytesPerBlock*width*height/16;
+- Mem sourceData( sourceDataSize );
+-
+- // read the source data
+- fread( sourceData.Get(), 1, sourceDataSize, sourceFile.Get() );
+-
+ // create the target rows
+- PngRows targetRows( width, height, 4 );
++ int const pixelSize = ( m_isColour ? 3 : 1 ) + ( m_isAlpha ? 1 : 0 );
++ PngRows rows( m_width*pixelSize, m_height );
+
+- // loop over blocks and compress them
+- u8 const* sourceBlock = sourceData.Get();
+- for( int y = 0; y < height; y += 4 )
+- {
+- // process a row of blocks
+- for( int x = 0; x < width; x += 4 )
+- {
+- // decompress back
+- u8 targetRgba[16*4];
+- Decompress( targetRgba, sourceBlock, flags );
+-
+- // write the data into the target rows
+- for( int py = 0, i = 0; py < 4; ++py )
+- {
+- u8* row = ( u8* )targetRows.Get()[y + py] + x*4;
+- for( int px = 0; px < 4; ++px, ++i )
+- {
+- for( int j = 0; j < 4; ++j )
+- *row++ = targetRgba[4*i + j];
+- }
++ // fill the rows with pixel data
++ u8 const *src = m_pixels.Get();
++ for( int y = 0; y < m_height; ++y )
++ {
++ u8 *dest = rows[y];
++ for( int x = 0; x < m_width; ++x )
++ {
++ if( m_isColour )
++ {
++ dest[0] = src[0];
++ dest[1] = src[1];
++ dest[2] = src[2];
++ dest += 3;
+ }
++ else
++ *dest++ = src[1];
+
+- // advance
+- sourceBlock += bytesPerBlock;
++ if( m_isAlpha )
++ *dest++ = src[3];
++
++ src += 4;
+ }
+ }
+
+- // create the target PNG
+- PngWriteStruct targetPng;
+-
+ // set up the image
++ PngWriteStruct png;
+ png_set_IHDR(
+- targetPng.GetPng(), targetPng.GetInfo(), width, height,
+- 8, PNG_COLOR_TYPE_RGBA, PNG_INTERLACE_NONE,
+- PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT
++ png.GetPng(), png.GetInfo(), m_width, m_height,
++ 8, ( m_isColour ? PNG_COLOR_MASK_COLOR : 0) | ( m_isAlpha ? PNG_COLOR_MASK_ALPHA : 0 ),
++ PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT
+ );
+
+ // open the target file
+- File targetFile( fopen( targetFileName.c_str(), "wb" ) );
+- if( !targetFile.IsValid() )
++ File file( fopen( fileName.c_str(), "wb" ) );
++ if( !file.IsValid() )
+ {
+ std::ostringstream oss;
+- oss << "failed to open \"" << targetFileName << "\" for writing";
++ oss << "failed to open \"" << fileName << "\" for writing";
+ throw Error( oss.str() );
+ }
+
+ // write the image
+- png_set_rows( targetPng.GetPng(), targetPng.GetInfo(), targetRows.Get() );
+- png_init_io( targetPng.GetPng(), targetFile.Get() );
+- png_write_png( targetPng.GetPng(), targetPng.GetInfo(), PNG_TRANSFORM_IDENTITY, 0 );
++ png_set_rows( png.GetPng(), png.GetInfo(), rows.Get() );
++ png_init_io( png.GetPng(), file.Get() );
++ png_write_png( png.GetPng(), png.GetInfo(), PNG_TRANSFORM_IDENTITY, 0 );
+ }
+
+-static void Diff( std::string const& sourceFileName, std::string const& targetFileName )
++void Image::Decompress( DxtData const& dxt )
+ {
+- // load the images
+- PngImage sourceImage( sourceFileName );
+- PngImage targetImage( targetFileName );
++ // allocate storage
++ m_width = dxt.width;
++ m_height = dxt.height;
++ m_isColour = dxt.isColour;
++ m_isAlpha = dxt.isAlpha;
++ m_pixels.Reset( 4*m_width*m_height );
+
+- // get the image info
+- int width = sourceImage.GetWidth();
+- int height = sourceImage.GetHeight();
+- int sourceStride = sourceImage.GetStride();
+- int targetStride = targetImage.GetStride();
+- int stride = std::min( sourceStride, targetStride );
++ // use the whole image decompression function to do the work
++ DecompressImage( m_pixels.Get(), m_width, m_height, dxt.data.Get(), dxt.format );
++}
++
++void Image::Compress( DxtData& dxt, int flags ) const
++{
++ // work out how much memory we need
++ int storageSize = GetStorageRequirements( m_width, m_height, flags );
+
+- // check they match
+- if( width != targetImage.GetWidth() || height != targetImage.GetHeight() )
+- throw Error( "source and target dimensions do not match" );
++ // set the structure fields and allocate it
++ dxt.width = m_width;
++ dxt.height = m_height;
++ dxt.format = flags & ( kDxt1 | kDxt3 | kDxt5 );
++ dxt.isColour = m_isColour;
++ dxt.isAlpha = m_isAlpha;
++ dxt.data.Reset( storageSize );
+
+- // work out the error
+- double error = 0.0;
+- for( int y = 0; y < height; ++y )
++ // use the whole image compression function to do the work
++ CompressImage( m_pixels.Get(), m_width, m_height, dxt.data.Get(), flags );
++}
++
++double Image::GetRmsError( Image const& image ) const
+ {
+- u8 const* sourceRow = sourceImage.GetRow( y );
+- u8 const* targetRow = targetImage.GetRow( y );
+- for( int x = 0; x < width; ++x )
++ if( m_width != image.m_width || m_height != image.m_height )
++ throw Error( "image dimensions mismatch when computing RMS error" );
++
++ // accumulate colour error
++ double difference = 0;
++ u8 const *a = m_pixels.Get();
++ u8 const *b = image.m_pixels.Get();
++ for( int y = 0; y < m_height; ++y )
+ {
+- u8 const* sourcePixel = sourceRow + x*sourceStride;
+- u8 const* targetPixel = targetRow + x*targetStride;
+- for( int i = 0; i < stride; ++i )
++ for( int x = 0; x < m_width; ++x )
+ {
+- int diff = ( int )sourcePixel[i] - ( int )targetPixel[i];
+- error += ( double )( diff*diff );
++ int d0 = ( int )a[0] - ( int )b[0];
++ int d1 = ( int )a[1] - ( int )b[1];
++ int d2 = ( int )a[2] - ( int )b[2];
++ difference += ( double )( d0*d0 + d1*d1 + d2*d2 );
++ a += 4;
++ b += 4;
+ }
+ }
++ return std::sqrt( difference/( double )( m_width*m_height ) );
+ }
+- error = std::sqrt( error / ( width*height ) );
+-
+- // print it out
+- std::cout << "rms error: " << error << std::endl;
+-}
+-
+-enum Mode
+-{
+- kCompress,
+- kDecompress,
+- kDiff
+-};
+
+ int main( int argc, char* argv[] )
+ {
+@@ -495,13 +448,12 @@
+ // parse the command-line
+ std::string sourceFileName;
+ std::string targetFileName;
+- Mode mode = kCompress;
+- int method = kDxt1;
+- int metric = kColourMetricPerceptual;
++ int format = kDxt1;
+ int fit = kColourClusterFit;
+ int extra = 0;
+ bool help = false;
+ bool arguments = true;
++ bool error = false;
+ for( int i = 1; i < argc; ++i )
+ {
+ // check for options
+@@ -513,20 +465,16 @@
+ switch( word[j] )
+ {
+ case 'h': help = true; break;
+- case 'c': mode = kCompress; break;
+- case 'd': mode = kDecompress; break;
+- case 'e': mode = kDiff; break;
+- case '1': method = kDxt1; break;
+- case '3': method = kDxt3; break;
+- case '5': method = kDxt5; break;
+- case 'u': metric = kColourMetricUniform; break;
++ case '1': format = kDxt1; break;
++ case '3': format = kDxt3; break;
++ case '5': format = kDxt5; break;
+ case 'r': fit = kColourRangeFit; break;
+ case 'i': fit = kColourIterativeClusterFit; break;
+ case 'w': extra = kWeightColourByAlpha; break;
+ case '-': arguments = false; break;
+ default:
+- std::cerr << "unknown option '" << word[j] << "'" << std::endl;
+- return -1;
++ std::cerr << "squishpng error: unknown option '" << word[j] << "'" << std::endl;
++ error = true;
+ }
+ }
+ }
+@@ -538,60 +486,53 @@
+ targetFileName.assign( word );
+ else
+ {
+- std::cerr << "unexpected argument \"" << word << "\"" << std::endl;
++ std::cerr << "squishpng error: unexpected argument \"" << word << "\"" << std::endl;
++ error = true;
+ }
+ }
+ }
+
+ // check arguments
+- if( help )
++ if( sourceFileName.empty() )
++ {
++ std::cerr << "squishpng error: no source file given" << std::endl;
++ error = true;
++ }
++ if( help || error )
+ {
+ std::cout
+ << "SYNTAX" << std::endl
+- << "\tsquishpng [-cde135] <source> <target>" << std::endl
++ << "\tsquishpng [-135riw] <source> [<target>]" << std::endl
+ << "OPTIONS" << std::endl
+- << "\t-c\tCompress source png to target raw dxt (default)" << std::endl
++ << "\t-h\tPrint this help message" << std::endl
+ << "\t-135\tSpecifies whether to use DXT1 (default), DXT3 or DXT5 compression" << std::endl
+- << "\t-u\tUse a uniform colour metric during colour compression" << std::endl
+ << "\t-r\tUse the fast but inferior range-based colour compressor" << std::endl
+ << "\t-i\tUse the very slow but slightly better iterative colour compressor" << std::endl
+ << "\t-w\tWeight colour values by alpha in the cluster colour compressor" << std::endl
+- << "\t-d\tDecompress source raw dxt to target png" << std::endl
+- << "\t-e\tDiff source and target png" << std::endl
+ ;
+
+- return 0;
+- }
+- if( sourceFileName.empty() )
+- {
+- std::cerr << "no source file given" << std::endl;
+- return -1;
+- }
+- if( targetFileName.empty() )
+- {
+- std::cerr << "no target file given" << std::endl;
+- return -1;
++ return error ? -1 : 0;
+ }
+
+- // do the work
+- switch( mode )
+- {
+- case kCompress:
+- Compress( sourceFileName, targetFileName, method | metric | fit | extra );
+- break;
++ // load the source image
++ Image sourceImage;
++ sourceImage.LoadPng( sourceFileName );
+
+- case kDecompress:
+- Decompress( sourceFileName, targetFileName, method );
+- break;
++ // compress to DXT
++ DxtData dxt;
++ sourceImage.Compress( dxt, format | fit | extra );
+
+- case kDiff:
+- Diff( sourceFileName, targetFileName );
+- break;
++ // decompress back
++ Image targetImage;
++ targetImage.Decompress( dxt );
+
+- default:
+- std::cerr << "unknown mode" << std::endl;
+- throw std::exception();
+- }
++ // compare the images
++ double rmsError = sourceImage.GetRmsError( targetImage );
++ std::cout << sourceFileName << " " << rmsError << std::endl;
++
++ // save the target image if necessary
++ if( !targetFileName.empty() )
++ targetImage.SavePng( targetFileName );
+ }
+ catch( std::exception& excuse )
+ {
+diff -uwNr squish-1.10/extra/squishtest.cpp libsquish-1.10-openelec/extra/squishtest.cpp
+--- squish-1.10/extra/squishtest.cpp 2006-03-03 21:20:22.000000000 +0100
++++ libsquish-1.10-openelec/extra/squishtest.cpp 2015-01-09 10:58:43.000000000 +0100
+@@ -35,6 +35,7 @@
+ #include <iostream>
+ #include <cmath>
+ #include <cfloat>
++#include <cstdlib>
+
+ using namespace squish;
+
+diff -uwNr squish-1.10/Makefile libsquish-1.10-openelec/Makefile
+--- squish-1.10/Makefile 2006-04-07 19:30:11.000000000 +0200
++++ libsquish-1.10-openelec/Makefile 2015-01-09 16:52:04.000000000 +0100
+@@ -7,11 +7,12 @@
+
+ LIB = libsquish.a
+
+-all : $(LIB)
++all : $(LIB) squish.pc
+
+-install : $(LIB)
++install : $(LIB) squish.pc
+ install squish.h $(INSTALL_DIR)/include
+ install libsquish.a $(INSTALL_DIR)/lib
++ install squish.pc $(INSTALL_DIR)/lib/pkgconfig
+
+ uninstall:
+ $(RM) $(INSTALL_DIR)/include/squish.h
+@@ -27,5 +28,6 @@
+ clean :
+ $(RM) $(OBJ) $(LIB)
+
+-
++squish.pc:
++ sed 's|@PREFIX@|$(PREFIX)|' $@.in > $@
+
+diff -uwNr squish-1.10/maths.cpp libsquish-1.10-openelec/maths.cpp
+--- squish-1.10/maths.cpp 2006-01-19 22:10:49.000000000 +0100
++++ libsquish-1.10-openelec/maths.cpp 2015-01-09 10:58:43.000000000 +0100
+@@ -30,6 +30,7 @@
+ */
+
+ #include "maths.h"
++#include "simd.h"
+ #include <cfloat>
+
+ namespace squish {
+@@ -44,6 +45,7 @@
+ total += weights[i];
+ centroid += weights[i]*points[i];
+ }
++ if( total > FLT_EPSILON )
+ centroid /= total;
+
+ // accumulate the covariance matrix
+@@ -65,6 +67,8 @@
+ return covariance;
+ }
+
++#if 0
++
+ static Vec3 GetMultiplicity1Evector( Sym3x3 const& matrix, float evalue )
+ {
+ // compute M
+@@ -224,4 +228,32 @@
+ }
+ }
+
++#else
++
++#define POWER_ITERATION_COUNT 8
++
++Vec3 ComputePrincipleComponent( Sym3x3 const& matrix )
++{
++ Vec4 const row0( matrix[0], matrix[1], matrix[2], 0.0f );
++ Vec4 const row1( matrix[1], matrix[3], matrix[4], 0.0f );
++ Vec4 const row2( matrix[2], matrix[4], matrix[5], 0.0f );
++ Vec4 v = VEC4_CONST( 1.0f );
++ for( int i = 0; i < POWER_ITERATION_COUNT; ++i )
++ {
++ // matrix multiply
++ Vec4 w = row0*v.SplatX();
++ w = MultiplyAdd(row1, v.SplatY(), w);
++ w = MultiplyAdd(row2, v.SplatZ(), w);
++
++ // get max component from xyz in all channels
++ Vec4 a = Max(w.SplatX(), Max(w.SplatY(), w.SplatZ()));
++
++ // divide through and advance
++ v = w*Reciprocal(a);
++ }
++ return v.GetVec3();
++}
++
++#endif
++
+ } // namespace squish
+diff -uwNr squish-1.10/rangefit.cpp libsquish-1.10-openelec/rangefit.cpp
+--- squish-1.10/rangefit.cpp 2006-09-02 10:48:17.000000000 +0200
++++ libsquish-1.10-openelec/rangefit.cpp 2015-01-09 10:58:43.000000000 +0100
+@@ -30,13 +30,12 @@
+
+ namespace squish {
+
+-RangeFit::RangeFit( ColourSet const* colours, int flags )
++RangeFit::RangeFit( ColourSet const* colours, int flags, float* metric )
+ : ColourFit( colours, flags )
+ {
+- // initialise the metric
+- bool perceptual = ( ( m_flags & kColourMetricPerceptual ) != 0 );
+- if( perceptual )
+- m_metric = Vec3( 0.2126f, 0.7152f, 0.0722f );
++ // initialise the metric (old perceptual = 0.2126f, 0.7152f, 0.0722f)
++ if( metric )
++ m_metric = Vec3( metric[0], metric[1], metric[2] );
+ else
+ m_metric = Vec3( 1.0f );
+
+diff -uwNr squish-1.10/rangefit.h libsquish-1.10-openelec/rangefit.h
+--- squish-1.10/rangefit.h 2006-07-25 16:28:13.000000000 +0200
++++ libsquish-1.10-openelec/rangefit.h 2015-01-09 10:58:43.000000000 +0100
+@@ -37,7 +37,7 @@
+ class RangeFit : public ColourFit
+ {
+ public:
+- RangeFit( ColourSet const* colours, int flags );
++ RangeFit( ColourSet const* colours, int flags, float* metric );
+
+ private:
+ virtual void Compress3( void* block );
+diff -uwNr squish-1.10/simd_ve.h libsquish-1.10-openelec/simd_ve.h
+--- squish-1.10/simd_ve.h 2007-03-21 20:31:51.000000000 +0100
++++ libsquish-1.10-openelec/simd_ve.h 2015-01-09 10:58:43.000000000 +0100
+@@ -31,7 +31,7 @@
+
+ namespace squish {
+
+-#define VEC4_CONST( X ) Vec4( ( vector float )( X ) )
++#define VEC4_CONST( X ) Vec4( ( vector float ){ X } )
+
+ class Vec4
+ {
+@@ -96,7 +96,7 @@
+
+ Vec4& operator*=( Arg v )
+ {
+- m_v = vec_madd( m_v, v.m_v, ( vector float )( -0.0f ) );
++ m_v = vec_madd( m_v, v.m_v, ( vector float ){ -0.0f } );
+ return *this;
+ }
+
+@@ -112,7 +112,7 @@
+
+ friend Vec4 operator*( Vec4::Arg left, Vec4::Arg right )
+ {
+- return Vec4( vec_madd( left.m_v, right.m_v, ( vector float )( -0.0f ) ) );
++ return Vec4( vec_madd( left.m_v, right.m_v, ( vector float ){ -0.0f } ) );
+ }
+
+ //! Returns a*b + c
+@@ -133,7 +133,7 @@
+ vector float estimate = vec_re( v.m_v );
+
+ // one round of Newton-Rhaphson refinement
+- vector float diff = vec_nmsub( estimate, v.m_v, ( vector float )( 1.0f ) );
++ vector float diff = vec_nmsub( estimate, v.m_v, ( vector float ){ 1.0f } );
+ return Vec4( vec_madd( diff, estimate, estimate ) );
+ }
+
+diff -uwNr squish-1.10/squish.cpp libsquish-1.10-openelec/squish.cpp
+--- squish-1.10/squish.cpp 2007-03-21 20:31:51.000000000 +0100
++++ libsquish-1.10-openelec/squish.cpp 2015-01-09 10:58:43.000000000 +0100
+@@ -23,6 +23,7 @@
+
+ -------------------------------------------------------------------------- */
+
++#include <string.h>
+ #include <squish.h>
+ #include "colourset.h"
+ #include "maths.h"
+@@ -39,28 +40,19 @@
+ // grab the flag bits
+ int method = flags & ( kDxt1 | kDxt3 | kDxt5 );
+ int fit = flags & ( kColourIterativeClusterFit | kColourClusterFit | kColourRangeFit );
+- int metric = flags & ( kColourMetricPerceptual | kColourMetricUniform );
+- int extra = flags & kWeightColourByAlpha;
++ int extra = flags & ( kWeightColourByAlpha | kSourceBGRA );
+
+ // set defaults
+ if( method != kDxt3 && method != kDxt5 )
+ method = kDxt1;
+- if( fit != kColourRangeFit )
++ if( fit != kColourRangeFit && fit != kColourIterativeClusterFit )
+ fit = kColourClusterFit;
+- if( metric != kColourMetricUniform )
+- metric = kColourMetricPerceptual;
+
+ // done
+- return method | fit | metric | extra;
++ return method | fit | extra;
+ }
+
+-void Compress( u8 const* rgba, void* block, int flags )
+-{
+- // compress with full mask
+- CompressMasked( rgba, 0xffff, block, flags );
+-}
+-
+-void CompressMasked( u8 const* rgba, int mask, void* block, int flags )
++void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric )
+ {
+ // fix any bad flags
+ flags = FixFlags( flags );
+@@ -84,13 +76,13 @@
+ else if( ( flags & kColourRangeFit ) != 0 || colours.GetCount() == 0 )
+ {
+ // do a range fit
+- RangeFit fit( &colours, flags );
++ RangeFit fit( &colours, flags, metric );
+ fit.Compress( colourBlock );
+ }
+ else
+ {
+ // default to a cluster fit (could be iterative or not)
+- ClusterFit fit( &colours, flags );
++ ClusterFit fit( &colours, flags, metric );
+ fit.Compress( colourBlock );
+ }
+
+@@ -133,7 +125,29 @@
+ return blockcount*blocksize;
+ }
+
+-void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags )
++void CopyRGBA( u8 const* source, u8* dest, int flags )
++{
++ if (flags & kSourceBGRA)
++ {
++ // convert from bgra to rgba
++ dest[0] = source[2];
++ dest[1] = source[1];
++ dest[2] = source[0];
++ dest[3] = source[3];
++ }
++ else
++ {
++ for( int i = 0; i < 4; ++i )
++ *dest++ = *source++;
++ }
++}
++
++void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric )
++{
++ CompressImage(rgba, width, height, width*4, blocks, flags, metric);
++}
++
++void CompressImage( u8 const* rgba, int width, int height, int pitch, void* blocks, int flags, float* metric )
+ {
+ // fix any bad flags
+ flags = FixFlags( flags );
+@@ -163,23 +177,17 @@
+ if( sx < width && sy < height )
+ {
+ // copy the rgba value
+- u8 const* sourcePixel = rgba + 4*( width*sy + sx );
+- for( int i = 0; i < 4; ++i )
+- *targetPixel++ = *sourcePixel++;
+-
++ u8 const* sourcePixel = rgba + pitch*sy + 4*sx;
++ CopyRGBA(sourcePixel, targetPixel, flags);
+ // enable this pixel
+ mask |= ( 1 << ( 4*py + px ) );
+ }
+- else
+- {
+- // skip this pixel as its outside the image
+ targetPixel += 4;
+ }
+ }
+- }
+
+ // compress it into the output
+- CompressMasked( sourceRgba, mask, targetBlock, flags );
++ CompressMasked( sourceRgba, mask, targetBlock, flags, metric );
+
+ // advance
+ targetBlock += bytesPerBlock;
+@@ -189,6 +197,11 @@
+
+ void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags )
+ {
++ DecompressImage( rgba, width, height, width*4, blocks, flags );
++}
++
++void DecompressImage( u8* rgba, int width, int height, int pitch, void const* blocks, int flags )
++{
+ // fix any bad flags
+ flags = FixFlags( flags );
+
+@@ -216,24 +229,132 @@
+ int sy = y + py;
+ if( sx < width && sy < height )
+ {
+- u8* targetPixel = rgba + 4*( width*sy + sx );
++ u8* targetPixel = rgba + pitch*sy + 4*sx;
+
+ // copy the rgba value
++ CopyRGBA(sourcePixel, targetPixel, flags);
++ }
++ sourcePixel += 4;
++ }
++ }
++
++ // advance
++ sourceBlock += bytesPerBlock;
++ }
++ }
++}
++
++static double ErrorSq(double x, double y)
++{
++ return (x - y) * (x - y);
++}
++
++static void ComputeBlockWMSE(u8 const *original, u8 const *compressed, unsigned int w, unsigned int h, double &cmse, double &amse)
++{
++ // Computes the MSE for the block and weights it by the variance of the original block.
++ // If the variance of the original block is less than 4 (i.e. a standard deviation of 1 per channel)
++ // then the block is close to being a single colour. Quantisation errors in single colour blocks
++ // are easier to see than similar errors in blocks that contain more colours, particularly when there
++ // are many such blocks in a large area (eg a blue sky background) as they cause banding. Given that
++ // banding is easier to see than small errors in "complex" blocks, we weight the errors by a factor
++ // of 5. This implies that images with large, single colour areas will have a higher potential WMSE
++ // than images with lots of detail.
++
++ cmse = amse = 0;
++ unsigned int sum_p[4]; // per channel sum of pixels
++ unsigned int sum_p2[4]; // per channel sum of pixels squared
++ memset(sum_p, 0, sizeof(sum_p));
++ memset(sum_p2, 0, sizeof(sum_p2));
++ for( unsigned int py = 0; py < 4; ++py )
++ {
++ for( unsigned int px = 0; px < 4; ++px )
++ {
++ if( px < w && py < h )
++ {
++ double pixelCMSE = 0;
++ for( int i = 0; i < 3; ++i )
++ {
++ pixelCMSE += ErrorSq(original[i], compressed[i]);
++ sum_p[i] += original[i];
++ sum_p2[i] += (unsigned int)original[i]*original[i];
++ }
++ if( original[3] == 0 && compressed[3] == 0 )
++ pixelCMSE = 0; // transparent in both, so colour is inconsequential
++ amse += ErrorSq(original[3], compressed[3]);
++ cmse += pixelCMSE;
++ sum_p[3] += original[3];
++ sum_p2[3] += (unsigned int)original[3]*original[3];
++ }
++ original += 4;
++ compressed += 4;
++ }
++ }
++ unsigned int variance = 0;
+ for( int i = 0; i < 4; ++i )
+- *targetPixel++ = *sourcePixel++;
++ variance += w*h*sum_p2[i] - sum_p[i]*sum_p[i];
++ if( variance < 4 * w * w * h * h )
++ {
++ amse *= 5;
++ cmse *= 5;
+ }
+- else
++}
++
++void ComputeMSE( u8 const *rgba, int width, int height, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE )
+ {
+- // skip this pixel as its outside the image
+- sourcePixel += 4;
++ ComputeMSE(rgba, width, height, width*4, dxt, flags, colourMSE, alphaMSE);
++}
++
++void ComputeMSE( u8 const *rgba, int width, int height, int pitch, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE )
++{
++ // fix any bad flags
++ flags = FixFlags( flags );
++ colourMSE = alphaMSE = 0;
++
++ // initialise the block input
++ squish::u8 const* sourceBlock = dxt;
++ int bytesPerBlock = ( ( flags & squish::kDxt1 ) != 0 ) ? 8 : 16;
++
++ // loop over blocks
++ for( int y = 0; y < height; y += 4 )
++ {
++ for( int x = 0; x < width; x += 4 )
++ {
++ // decompress the block
++ u8 targetRgba[4*16];
++ Decompress( targetRgba, sourceBlock, flags );
++ u8 const* sourcePixel = targetRgba;
++
++ // copy across to a similar pixel block
++ u8 originalRgba[4*16];
++ u8* originalPixel = originalRgba;
++
++ for( int py = 0; py < 4; ++py )
++ {
++ for( int px = 0; px < 4; ++px )
++ {
++ int sx = x + px;
++ int sy = y + py;
++ if( sx < width && sy < height )
++ {
++ u8 const* targetPixel = rgba + pitch*sy + 4*sx;
++ CopyRGBA(targetPixel, originalPixel, flags);
+ }
++ sourcePixel += 4;
++ originalPixel += 4;
+ }
+ }
+
++ // compute the weighted MSE of the block
++ double blockCMSE, blockAMSE;
++ ComputeBlockWMSE(originalRgba, targetRgba, std::min(4, width - x), std::min(4, height - y), blockCMSE, blockAMSE);
++ colourMSE += blockCMSE;
++ alphaMSE += blockAMSE;
+ // advance
+ sourceBlock += bytesPerBlock;
+ }
+ }
++ colourMSE /= (width * height * 3);
++ alphaMSE /= (width * height);
+ }
+
+ } // namespace squish
+diff -uwNr squish-1.10/squish.h libsquish-1.10-openelec/squish.h
+--- squish-1.10/squish.h 2007-03-21 21:13:51.000000000 +0100
++++ libsquish-1.10-openelec/squish.h 2015-01-09 10:58:43.000000000 +0100
+@@ -56,14 +56,11 @@
+ //! Use a fast but low quality colour compressor.
+ kColourRangeFit = ( 1 << 4 ),
+
+- //! Use a perceptual metric for colour error (the default).
+- kColourMetricPerceptual = ( 1 << 5 ),
+-
+- //! Use a uniform metric for colour error.
+- kColourMetricUniform = ( 1 << 6 ),
+-
+ //! Weight the colour by alpha during cluster fit (disabled by default).
+- kWeightColourByAlpha = ( 1 << 7 )
++ kWeightColourByAlpha = ( 1 << 7 ),
++
++ //! Source is BGRA rather than RGBA
++ kSourceBGRA = ( 1 << 9 ),
+ };
+
+ // -----------------------------------------------------------------------------
+@@ -71,74 +68,90 @@
+ /*! @brief Compresses a 4x4 block of pixels.
+
+ @param rgba The rgba values of the 16 source pixels.
++ @param mask The valid pixel mask.
+ @param block Storage for the compressed DXT block.
+ @param flags Compression flags.
++ @param metric An optional perceptual metric.
+
+ The source pixels should be presented as a contiguous array of 16 rgba
+ values, with each component as 1 byte each. In memory this should be:
+
+ { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
+
++ The mask parameter enables only certain pixels within the block. The lowest
++ bit enables the first pixel and so on up to the 16th bit. Bits beyond the
++ 16th bit are ignored. Pixels that are not enabled are allowed to take
++ arbitrary colours in the output block. An example of how this can be used
++ is in the CompressImage function to disable pixels outside the bounds of
++ the image when the width or height is not divisible by 4.
++
+ The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression,
+ however, DXT1 will be used by default if none is specified. When using DXT1
+ compression, 8 bytes of storage are required for the compressed DXT block.
+ DXT3 and DXT5 compression require 16 bytes of storage per block.
+
+- The flags parameter can also specify a preferred colour compressor and
+- colour error metric to use when fitting the RGB components of the data.
+- Possible colour compressors are: kColourClusterFit (the default),
+- kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics
+- are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no
+- flags are specified in any particular category then the default will be
+- used. Unknown flags are ignored.
+-
+- When using kColourClusterFit, an additional flag can be specified to
+- weight the colour of each pixel by its alpha value. For images that are
+- rendered using alpha blending, this can significantly increase the
+- perceived quality.
++ The flags parameter can also specify a preferred colour compressor to use
++ when fitting the RGB components of the data. Possible colour compressors
++ are: kColourClusterFit (the default), kColourRangeFit (very fast, low
++ quality) or kColourIterativeClusterFit (slowest, best quality).
++
++ When using kColourClusterFit or kColourIterativeClusterFit, an additional
++ flag can be specified to weight the importance of each pixel by its alpha
++ value. For images that are rendered using alpha blending, this can
++ significantly increase the perceived quality.
++
++ The metric parameter can be used to weight the relative importance of each
++ colour channel, or pass NULL to use the default uniform weight of
++ { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
++ allowed either uniform or "perceptual" weights with the fixed values
++ { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
++ contiguous array of 3 floats.
+ */
+-void Compress( u8 const* rgba, void* block, int flags );
++void CompressMasked( u8 const* rgba, int mask, void* block, int flags, float* metric = 0 );
+
+ // -----------------------------------------------------------------------------
+
+ /*! @brief Compresses a 4x4 block of pixels.
+
+ @param rgba The rgba values of the 16 source pixels.
+- @param mask The valid pixel mask.
+ @param block Storage for the compressed DXT block.
+ @param flags Compression flags.
++ @param metric An optional perceptual metric.
+
+ The source pixels should be presented as a contiguous array of 16 rgba
+ values, with each component as 1 byte each. In memory this should be:
+
+ { r1, g1, b1, a1, .... , r16, g16, b16, a16 }
+
+- The mask parameter enables only certain pixels within the block. The lowest
+- bit enables the first pixel and so on up to the 16th bit. Bits beyond the
+- 16th bit are ignored. Pixels that are not enabled are allowed to take
+- arbitrary colours in the output block. An example of how this can be used
+- is in the CompressImage function to disable pixels outside the bounds of
+- the image when the width or height is not divisible by 4.
+-
+ The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression,
+ however, DXT1 will be used by default if none is specified. When using DXT1
+ compression, 8 bytes of storage are required for the compressed DXT block.
+ DXT3 and DXT5 compression require 16 bytes of storage per block.
+
+- The flags parameter can also specify a preferred colour compressor and
+- colour error metric to use when fitting the RGB components of the data.
+- Possible colour compressors are: kColourClusterFit (the default),
+- kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics
+- are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no
+- flags are specified in any particular category then the default will be
+- used. Unknown flags are ignored.
+-
+- When using kColourClusterFit, an additional flag can be specified to
+- weight the colour of each pixel by its alpha value. For images that are
+- rendered using alpha blending, this can significantly increase the
+- perceived quality.
+-*/
+-void CompressMasked( u8 const* rgba, int mask, void* block, int flags );
++ The flags parameter can also specify a preferred colour compressor to use
++ when fitting the RGB components of the data. Possible colour compressors
++ are: kColourClusterFit (the default), kColourRangeFit (very fast, low
++ quality) or kColourIterativeClusterFit (slowest, best quality).
++
++ When using kColourClusterFit or kColourIterativeClusterFit, an additional
++ flag can be specified to weight the importance of each pixel by its alpha
++ value. For images that are rendered using alpha blending, this can
++ significantly increase the perceived quality.
++
++ The metric parameter can be used to weight the relative importance of each
++ colour channel, or pass NULL to use the default uniform weight of
++ { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
++ allowed either uniform or "perceptual" weights with the fixed values
++ { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
++ contiguous array of 3 floats.
++
++ This method is an inline that calls CompressMasked with a mask of 0xffff,
++ provided for compatibility with older versions of squish.
++*/
++inline void Compress( u8 const* rgba, void* block, int flags, float* metric = 0 )
++{
++ CompressMasked( rgba, 0xffff, block, flags, metric );
++}
+
+ // -----------------------------------------------------------------------------
+
+@@ -184,8 +197,10 @@
+ @param rgba The pixels of the source.
+ @param width The width of the source image.
+ @param height The height of the source image.
++ @param pitch The pitch of the source image.
+ @param blocks Storage for the compressed output.
+ @param flags Compression flags.
++ @param metric An optional perceptual metric.
+
+ The source pixels should be presented as a contiguous array of width*height
+ rgba values, with each component as 1 byte each. In memory this should be:
+@@ -197,24 +212,30 @@
+ compression, 8 bytes of storage are required for each compressed DXT block.
+ DXT3 and DXT5 compression require 16 bytes of storage per block.
+
+- The flags parameter can also specify a preferred colour compressor and
+- colour error metric to use when fitting the RGB components of the data.
+- Possible colour compressors are: kColourClusterFit (the default),
+- kColourRangeFit or kColourIterativeClusterFit. Possible colour error metrics
+- are: kColourMetricPerceptual (the default) or kColourMetricUniform. If no
+- flags are specified in any particular category then the default will be
+- used. Unknown flags are ignored.
+-
+- When using kColourClusterFit, an additional flag can be specified to
+- weight the colour of each pixel by its alpha value. For images that are
+- rendered using alpha blending, this can significantly increase the
+- perceived quality.
+-
+- Internally this function calls squish::Compress for each block. To see how
+- much memory is required in the compressed image, use
+- squish::GetStorageRequirements.
++ The flags parameter can also specify a preferred colour compressor to use
++ when fitting the RGB components of the data. Possible colour compressors
++ are: kColourClusterFit (the default), kColourRangeFit (very fast, low
++ quality) or kColourIterativeClusterFit (slowest, best quality).
++
++ When using kColourClusterFit or kColourIterativeClusterFit, an additional
++ flag can be specified to weight the importance of each pixel by its alpha
++ value. For images that are rendered using alpha blending, this can
++ significantly increase the perceived quality.
++
++ The metric parameter can be used to weight the relative importance of each
++ colour channel, or pass NULL to use the default uniform weight of
++ { 1.0f, 1.0f, 1.0f }. This replaces the previous flag-based control that
++ allowed either uniform or "perceptual" weights with the fixed values
++ { 0.2126f, 0.7152f, 0.0722f }. If non-NULL, the metric should point to a
++ contiguous array of 3 floats.
++
++ Internally this function calls squish::CompressMasked for each block, which
++ allows for pixels outside the image to take arbitrary values. The function
++ squish::GetStorageRequirements can be called to compute the amount of memory
++ to allocate for the compressed output.
+ */
+-void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags );
++void CompressImage( u8 const* rgba, int width, int height, void* blocks, int flags, float* metric = 0 );
++void CompressImage( u8 const* rgba, int width, int height, int pitch, void* blocks, int flags, float* metric = 0 );
+
+ // -----------------------------------------------------------------------------
+
+@@ -223,6 +244,7 @@
+ @param rgba Storage for the decompressed pixels.
+ @param width The width of the source image.
+ @param height The height of the source image.
++ @param pitch The pitch of the decompressed pixels.
+ @param blocks The compressed DXT blocks.
+ @param flags Compression flags.
+
+@@ -238,6 +260,32 @@
+ Internally this function calls squish::Decompress for each block.
+ */
+ void DecompressImage( u8* rgba, int width, int height, void const* blocks, int flags );
++void DecompressImage( u8* rgba, int width, int height, int pitch, void const* blocks, int flags );
++
++// -----------------------------------------------------------------------------
++
++/*! @brief Computes MSE of an compressed image in memory.
++
++ @param rgba The original image pixels.
++ @param width The width of the source image.
++ @param height The height of the source image.
++ @param pitch The pitch of the source image.
++ @param dxt The compressed dxt blocks
++ @param flags Compression flags.
++ @param colourMSE The MSE of the colour values.
++ @param alphaMSE The MSE of the alpha values.
++
++ The colour MSE and alpha MSE are computed across all pixels. The colour MSE is
++ averaged across all rgb values (i.e. colourMSE = sum sum_k ||dxt.k - rgba.k||/3)
++
++ The flags parameter should specify either kDxt1, kDxt3 or kDxt5 compression,
++ however, DXT1 will be used by default if none is specified. All other flags
++ are ignored.
++
++ Internally this function calls squish::Decompress for each block.
++*/
++void ComputeMSE(u8 const *rgba, int width, int height, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE);
++void ComputeMSE(u8 const *rgba, int width, int height, int pitch, u8 const *dxt, int flags, double &colourMSE, double &alphaMSE);
+
+ // -----------------------------------------------------------------------------
+
+diff -uwNr squish-1.10/squish.pc.in libsquish-1.10-openelec/squish.pc.in
+--- squish-1.10/squish.pc.in 1970-01-01 01:00:00.000000000 +0100
++++ libsquish-1.10-openelec/squish.pc.in 2015-01-09 10:58:43.000000000 +0100
+@@ -0,0 +1,13 @@
++prefix=@PREFIX@
++exec_prefix=${prefix}
++libdir=${prefix}/lib
++sharedlibdir=${libdir}
++includedir=${prefix}/include
++
++Name: squish
++Description: squish DXT lib
++Version: 1.0.0-kodi
++
++Requires:
++Libs: -L${libdir} -L${sharedlibdir} -lsquish
++Cflags: -I${includedir}
diff --git a/package/libsquish/Config.in b/package/libsquish/Config.in
new file mode 100644
index 0000000..10af979
--- /dev/null
+++ b/package/libsquish/Config.in
@@ -0,0 +1,11 @@
+config BR2_PACKAGE_LIBSQUISH
+ bool "libsquish"
+ depends on BR2_INSTALL_LIBSTDCPP
+ help
+ The squish library (abbreviated to libsquish) is an open source DXT
+ compression library written in C++
+
+ https://code.google.com/p/libsquish
+
+comment "libsquish needs a toolchain w/ C++"
+ depends on !BR2_INSTALL_LIBSTDCPP
diff --git a/package/libsquish/libsquish.hash b/package/libsquish/libsquish.hash
new file mode 100644
index 0000000..f582b92
--- /dev/null
+++ b/package/libsquish/libsquish.hash
@@ -0,0 +1,2 @@
+# From https://code.google.com/p/libsquish/downloads/detail?name=squish-1.10.tar.gz
+sha1 b15e6b970cd0bfd475848c5d1ceaf97cdd98e1b0 squish-1.10.tar.gz
diff --git a/package/libsquish/libsquish.mk b/package/libsquish/libsquish.mk
new file mode 100644
index 0000000..29da627
--- /dev/null
+++ b/package/libsquish/libsquish.mk
@@ -0,0 +1,27 @@
+################################################################################
+#
+# libsquish
+#
+################################################################################
+
+LIBSQUISH_VERSION = 1.10
+LIBSQUISH_SOURCE = squish-$(LIBSQUISH_VERSION).tar.gz
+LIBSQUISH_SITE = https://libsquish.googlecode.com/files
+LIBSQUISH_INSTALL_STAGING = YES
+LIBSQUISH_LICENSE = MIT
+LIBSQUISH_LICENSE_FILES = README
+
+define LIBSQUISH_INSTALL_STAGING_CMDS
+ mkdir -p $(STAGING_DIR)/usr/lib/pkgconfig/
+ CXX=$(TARGET_CXX) CXXFLAGS="$(TARGET_CXXFLAGS)" \
+ $(MAKE) -C $(@D) install PREFIX=/usr INSTALL_DIR=$(STAGING_DIR)/usr
+endef
+
+define LIBSQUISH_INSTALL_TARGET_CMDS
+ mkdir -p $(TARGET_DIR)/usr/include/
+ mkdir -p $(TARGET_DIR)/usr/lib/pkgconfig/
+ CXX=$(TARGET_CXX) CXXFLAGS="$(TARGET_CXXFLAGS)" \
+ $(MAKE) -C $(@D) install PREFIX=/usr INSTALL_DIR=$(TARGET_DIR)/usr
+endef
+
+$(eval $(generic-package))
--
1.7.10.4
More information about the buildroot
mailing list