summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--HISTORY3
-rw-r--r--Makefile2
-rw-r--r--icons/default/skinElements/menubutton.pngbin49719 -> 9360 bytes
-rw-r--r--icons/default/skinElements/menubuttonactive.pngbin53449 -> 11656 bytes
-rw-r--r--icons/default/skinIcons/DiskUsage.pngbin2835 -> 4404 bytes
-rw-r--r--imagecache.c136
-rw-r--r--imagemagickwrapper.c41
-rw-r--r--imagemagickwrapper.h2
-rw-r--r--imagescaler.c149
-rw-r--r--imagescaler.h97
10 files changed, 317 insertions, 113 deletions
diff --git a/HISTORY b/HISTORY
index 6342a15..49924e2 100644
--- a/HISTORY
+++ b/HISTORY
@@ -377,3 +377,6 @@ Version 0.1.4
- Fixed a bug that setup values are not stored if theme is changed
Version 1.0.0
+
+- improved image scaling by using internal scaling libary from
+ Nikolaus Meine (thanks @Soeren Moch for providing the patch)
diff --git a/Makefile b/Makefile
index d6b3afa..86c1531 100644
--- a/Makefile
+++ b/Makefile
@@ -64,7 +64,7 @@ endif
### The object files (add further files here):
OBJS = $(PLUGIN).o config.o displaychannel.o displaymenu.o displaymenuview.o displaymessage.o displayreplay.o displaytracks.o displayvolume.o displaychannelview.o fontmanager.o \
- geometrymanager.o imagecache.o imagemagickwrapper.o helpers.o imageloader.o menudetailview.o menuitem.o nopacity.o setup.o textwindow.o timers.o
+ geometrymanager.o imagecache.o imagemagickwrapper.o imagescaler.o helpers.o imageloader.o menudetailview.o menuitem.o nopacity.o setup.o textwindow.o timers.o
### The main target:
diff --git a/icons/default/skinElements/menubutton.png b/icons/default/skinElements/menubutton.png
index 505e226..0800a5e 100644
--- a/icons/default/skinElements/menubutton.png
+++ b/icons/default/skinElements/menubutton.png
Binary files differ
diff --git a/icons/default/skinElements/menubuttonactive.png b/icons/default/skinElements/menubuttonactive.png
index 859a85a..caae104 100644
--- a/icons/default/skinElements/menubuttonactive.png
+++ b/icons/default/skinElements/menubuttonactive.png
Binary files differ
diff --git a/icons/default/skinIcons/DiskUsage.png b/icons/default/skinIcons/DiskUsage.png
index de1dfbb..f40fcd9 100644
--- a/icons/default/skinIcons/DiskUsage.png
+++ b/icons/default/skinIcons/DiskUsage.png
Binary files differ
diff --git a/imagecache.c b/imagecache.c
index 52a7992..29690c3 100644
--- a/imagecache.c
+++ b/imagecache.c
@@ -51,9 +51,6 @@ void cImageCache::CreateCache(void) {
if (success) {
channelsCached++;
InsertIntoLogoCache(ctLogo, *channel->GetChannelID().ToString());
- }
- success = LoadLogo(channel);
- if (success) {
InsertIntoLogoCache(ctLogoMenuItem, *channel->GetChannelID().ToString());
}
}
@@ -151,12 +148,11 @@ cImage *cImageCache::GetLogo(eCacheType type, const cChannel *channel) {
cPoint logoSize = LogoSize(type);
int width = logoSize.X();
int height = logoSize.Y();
- buffer.sample(Geometry(width, height));
if (tempStaticLogo) {
delete tempStaticLogo;
tempStaticLogo = NULL;
}
- tempStaticLogo = CreateImage();
+ tempStaticLogo = CreateImage(width, height);
return tempStaticLogo;
} else {
//add requested logo to cache
@@ -324,10 +320,7 @@ bool cImageCache::LoadIcon(eCacheType type, std::string name) {
}
void cImageCache::InsertIntoIconCache(eCacheType type, std::string name, int width, int height, bool preserveAspect) {
- Geometry size(width, height);
- size.aspect(!preserveAspect);
- buffer.sample(size);
- cImage *image = CreateImage();
+ cImage *image = CreateImage(width, height, preserveAspect);
if (type == ctMenuIcon)
menuIconCache.insert(std::pair<std::string, cImage*>(name, image));
else if (type == ctSkinIcon)
@@ -376,8 +369,7 @@ void cImageCache::InsertIntoLogoCache(eCacheType type, std::string channelID) {
cPoint logoSize = LogoSize(type);
int width = logoSize.X();
int height = logoSize.Y();
- buffer.sample(Geometry(width, height));
- cImage *image = CreateImage();
+ cImage *image = CreateImage(width, height);
if (type == ctLogo)
logoCache.insert(std::pair<std::string, cImage*>(channelID, image));
else if (type == ctLogoMenuItem)
@@ -496,82 +488,37 @@ void cImageCache::CreateSkinElementsGraphics(void) {
std::string imgMenuItem = "skinElements/menubutton";
std::string imgMenuItemActive = "skinElements/menubuttonactive";
std::string imgMenuItemTop = "skinElements/menubuttontop";
- //Main Menu
+ //Main Menu + Setup Menu + Schedules Menu + Channels Menu + Recordings Menu + Timers Menu + Tracks Menu
success = LoadIcon(ctSkinElement, imgMenuItem);
- if (success)
+ if (success) {
InsertIntoSkinElementCache(seMain, geoManager->menuItemWidthMain, geoManager->menuItemHeightMain);
- success = LoadIcon(ctSkinElement, imgMenuItemActive);
- if (success)
- InsertIntoSkinElementCache(seMainHigh, geoManager->menuItemWidthMain, geoManager->menuItemHeightMain);
- success = LoadIcon(ctSkinElement, imgMenuItemTop);
- if (success)
- InsertIntoSkinElementCache(seMainTop, geoManager->menuItemWidthMain, geoManager->menuItemHeightMain);
-
- //Setup Menu
- success = LoadIcon(ctSkinElement, imgMenuItem);
- if (success)
InsertIntoSkinElementCache(seSetup, geoManager->menuItemWidthSetup, geoManager->menuItemHeightMain);
- success = LoadIcon(ctSkinElement, imgMenuItemActive);
- if (success)
- InsertIntoSkinElementCache(seSetupHigh, geoManager->menuItemWidthSetup, geoManager->menuItemHeightMain);
- success = LoadIcon(ctSkinElement, imgMenuItemTop);
- if (success)
- InsertIntoSkinElementCache(seSetupTop, geoManager->menuItemWidthSetup, geoManager->menuItemHeightMain);
-
- //Schedules Menu
- success = LoadIcon(ctSkinElement, imgMenuItem);
- if (success)
InsertIntoSkinElementCache(seSchedules, geoManager->menuItemWidthSchedule, geoManager->menuItemHeightSchedule);
- success = LoadIcon(ctSkinElement, imgMenuItemActive);
- if (success)
- InsertIntoSkinElementCache(seSchedulesHigh, geoManager->menuItemWidthSchedule, geoManager->menuItemHeightSchedule);
- success = LoadIcon(ctSkinElement, imgMenuItemTop);
- if (success)
- InsertIntoSkinElementCache(seSchedulesTop, geoManager->menuItemWidthSchedule, geoManager->menuItemHeightSchedule);
-
- //Channels Menu
- success = LoadIcon(ctSkinElement, imgMenuItem);
- if (success)
InsertIntoSkinElementCache(seChannels, geoManager->menuItemWidthChannel, geoManager->menuItemHeightSchedule);
- success = LoadIcon(ctSkinElement, imgMenuItemActive);
- if (success)
- InsertIntoSkinElementCache(seChannelsHigh, geoManager->menuItemWidthChannel, geoManager->menuItemHeightSchedule);
- success = LoadIcon(ctSkinElement, imgMenuItemTop);
- if (success)
- InsertIntoSkinElementCache(seChannelsTop, geoManager->menuItemWidthChannel, geoManager->menuItemHeightSchedule);
-
- //Recordings Menu
- success = LoadIcon(ctSkinElement, imgMenuItem);
- if (success)
InsertIntoSkinElementCache(seRecordings, geoManager->menuItemWidthRecording, geoManager->menuItemHeightRecordings);
- success = LoadIcon(ctSkinElement, imgMenuItemActive);
- if (success)
- InsertIntoSkinElementCache(seRecordingsHigh, geoManager->menuItemWidthRecording, geoManager->menuItemHeightRecordings);
- success = LoadIcon(ctSkinElement, imgMenuItemTop);
- if (success)
- InsertIntoSkinElementCache(seRecordingsTop, geoManager->menuItemWidthRecording, geoManager->menuItemHeightRecordings);
-
- //Timers Menu
- success = LoadIcon(ctSkinElement, imgMenuItem);
- if (success)
InsertIntoSkinElementCache(seTimers, geoManager->menuItemWidthTimer, geoManager->menuItemHeightSchedule);
- success = LoadIcon(ctSkinElement, imgMenuItemActive);
- if (success)
- InsertIntoSkinElementCache(seTimersHigh, geoManager->menuItemWidthTimer, geoManager->menuItemHeightSchedule);
- success = LoadIcon(ctSkinElement, imgMenuItemTop);
- if (success)
- InsertIntoSkinElementCache(seTimersTop, geoManager->menuItemWidthTimer, geoManager->menuItemHeightSchedule);
-
- //Tracks Menu
- success = LoadIcon(ctSkinElement, imgMenuItem);
- if (success)
InsertIntoSkinElementCache(seTracks, geoManager->menuItemWidthTracks, geoManager->menuItemHeightTracks);
+ }
success = LoadIcon(ctSkinElement, imgMenuItemActive);
- if (success)
+ if (success) {
+ InsertIntoSkinElementCache(seMainHigh, geoManager->menuItemWidthMain, geoManager->menuItemHeightMain);
+ InsertIntoSkinElementCache(seSetupHigh, geoManager->menuItemWidthSetup, geoManager->menuItemHeightMain);
+ InsertIntoSkinElementCache(seSchedulesHigh, geoManager->menuItemWidthSchedule, geoManager->menuItemHeightSchedule);
+ InsertIntoSkinElementCache(seChannelsHigh, geoManager->menuItemWidthChannel, geoManager->menuItemHeightSchedule);
+ InsertIntoSkinElementCache(seRecordingsHigh, geoManager->menuItemWidthRecording, geoManager->menuItemHeightRecordings);
+ InsertIntoSkinElementCache(seTimersHigh, geoManager->menuItemWidthTimer, geoManager->menuItemHeightSchedule);
InsertIntoSkinElementCache(seTracksHigh, geoManager->menuItemWidthTracks, geoManager->menuItemHeightTracks);
+ }
success = LoadIcon(ctSkinElement, imgMenuItemTop);
- if (success)
+ if (success) {
+ InsertIntoSkinElementCache(seMainTop, geoManager->menuItemWidthMain, geoManager->menuItemHeightMain);
+ InsertIntoSkinElementCache(seSetupTop, geoManager->menuItemWidthSetup, geoManager->menuItemHeightMain);
+ InsertIntoSkinElementCache(seSchedulesTop, geoManager->menuItemWidthSchedule, geoManager->menuItemHeightSchedule);
+ InsertIntoSkinElementCache(seChannelsTop, geoManager->menuItemWidthChannel, geoManager->menuItemHeightSchedule);
+ InsertIntoSkinElementCache(seRecordingsTop, geoManager->menuItemWidthRecording, geoManager->menuItemHeightRecordings);
+ InsertIntoSkinElementCache(seTimersTop, geoManager->menuItemWidthTimer, geoManager->menuItemHeightSchedule);
InsertIntoSkinElementCache(seTracksTop, geoManager->menuItemWidthTracks, geoManager->menuItemHeightTracks);
+ }
//Color Buttons
std::string imgButtonRed = "skinElements/buttonred";
@@ -608,37 +555,31 @@ void cImageCache::CreateSkinElementsGraphics(void) {
if (success)
InsertIntoSkinElementCache(seMenuHeader, geoManager->osdWidth, geoManager->menuHeaderHeight);
- //Menu Messages Background
+ //Messages Background + Menu Messages Background
std::string msgStatus = "skinElements/messageStatus";
std::string msgInfo = "skinElements/messageInfo";
std::string msgWarning = "skinElements/messageWarning";
std::string msgError = "skinElements/messageError";
success = LoadIcon(ctSkinElement, msgStatus);
- if (success)
- InsertIntoSkinElementCache(seMessageMenuStatus, geoManager->menuMessageWidth, geoManager->menuMessageHeight);
- success = LoadIcon(ctSkinElement, msgInfo);
- if (success)
- InsertIntoSkinElementCache(seMessageMenuInfo, geoManager->menuMessageWidth, geoManager->menuMessageHeight);
- success = LoadIcon(ctSkinElement, msgWarning);
- if (success)
- InsertIntoSkinElementCache(seMessageMenuWarning, geoManager->menuMessageWidth, geoManager->menuMessageHeight);
- success = LoadIcon(ctSkinElement, msgError);
- if (success)
- InsertIntoSkinElementCache(seMessageMenuError, geoManager->menuMessageWidth, geoManager->menuMessageHeight);
- //Messages Background
- success = LoadIcon(ctSkinElement, msgStatus);
- if (success)
+ if (success) {
InsertIntoSkinElementCache(seMessageStatus, geoManager->messageWidth, geoManager->messageHeight);
+ InsertIntoSkinElementCache(seMessageMenuStatus, geoManager->menuMessageWidth, geoManager->menuMessageHeight);
+ }
success = LoadIcon(ctSkinElement, msgInfo);
- if (success)
+ if (success) {
InsertIntoSkinElementCache(seMessageInfo, geoManager->messageWidth, geoManager->messageHeight);
+ InsertIntoSkinElementCache(seMessageMenuInfo, geoManager->menuMessageWidth, geoManager->menuMessageHeight);
+ }
success = LoadIcon(ctSkinElement, msgWarning);
- if (success)
+ if (success) {
InsertIntoSkinElementCache(seMessageWarning, geoManager->messageWidth, geoManager->messageHeight);
+ InsertIntoSkinElementCache(seMessageMenuWarning, geoManager->menuMessageWidth, geoManager->menuMessageHeight);
+ }
success = LoadIcon(ctSkinElement, msgError);
- if (success)
+ if (success) {
InsertIntoSkinElementCache(seMessageError, geoManager->messageWidth, geoManager->messageHeight);
-
+ InsertIntoSkinElementCache(seMessageMenuError, geoManager->menuMessageWidth, geoManager->menuMessageHeight);
+ }
//DisplayChannel Background and Foreground
std::string imgChannelBackground;
@@ -695,12 +636,7 @@ void cImageCache::CreateSkinElementsGraphics(void) {
}
void cImageCache::InsertIntoSkinElementCache(eSkinElementType type, int width, int height) {
- if (width>0 && height>0) {
- Geometry size(width, height);
- size.aspect(true);
- buffer.sample(size);
- }
- cImage *image = CreateImage();
+ cImage *image = CreateImage(width, height, false);
skinElementCache.insert(std::pair<eSkinElementType, cImage*>(type, image));
}
diff --git a/imagemagickwrapper.c b/imagemagickwrapper.c
index 7cdf980..0660a69 100644
--- a/imagemagickwrapper.c
+++ b/imagemagickwrapper.c
@@ -2,6 +2,7 @@
#include <sstream>
#include "imagemagickwrapper.h"
#include "config.h"
+#include "imagescaler.h"
cImageMagickWrapper::cImageMagickWrapper() {
InitializeMagick(NULL);
@@ -10,22 +11,40 @@ cImageMagickWrapper::cImageMagickWrapper() {
cImageMagickWrapper::~cImageMagickWrapper() {
}
-cImage *cImageMagickWrapper::CreateImage() {
+cImage *cImageMagickWrapper::CreateImage(int width, int height, bool preserveAspect) {
int w, h;
w = buffer.columns();
h = buffer.rows();
- cImage *image = new cImage(cSize(w, h));
+ if (width == 0)
+ width = w;
+ if (height == 0)
+ height = h;
+ if (preserveAspect) {
+ unsigned scale_w = 1000 * width / w;
+ unsigned scale_h = 1000 * height / h;
+ if (scale_w > scale_h)
+ width = w * height / h;
+ else
+ height = h * width / w;
+ }
const PixelPacket *pixels = buffer.getConstPixels(0, 0, w, h);
- for (int iy = 0; iy < h; ++iy) {
- for (int ix = 0; ix < w; ++ix) {
- tColor col = (~int(pixels->opacity * 255 / MaxRGB) << 24)
- | (int(pixels->green * 255 / MaxRGB) << 8)
- | (int(pixels->red * 255 / MaxRGB) << 16)
- | (int(pixels->blue * 255 / MaxRGB) );
- image->SetPixel(cPoint(ix, iy), col);
- ++pixels;
- }
+ cImage *image = new cImage(cSize(width, height));
+ tColor *imgData = (tColor *)image->Data();
+ if (w != width || h != height) {
+ ImageScaler scaler;
+ scaler.SetImageParameters(imgData, width, width, height, w, h);
+ for (const void *pixels_end = &pixels[w*h]; pixels < pixels_end; ++pixels)
+ scaler.PutSourcePixel(pixels->blue / ((MaxRGB + 1) / 256),
+ pixels->green / ((MaxRGB + 1) / 256),
+ pixels->red / ((MaxRGB + 1) / 256),
+ ~(pixels->opacity / ((MaxRGB + 1) / 256)));
+ return image;
}
+ for (const void *pixels_end = &pixels[width*height]; pixels < pixels_end; ++pixels)
+ *imgData++ = ((~int(pixels->opacity / ((MaxRGB + 1) / 256)) << 24) |
+ (int(pixels->green / ((MaxRGB + 1) / 256)) << 8) |
+ (int(pixels->red / ((MaxRGB + 1) / 256)) << 16) |
+ (int(pixels->blue / ((MaxRGB + 1) / 256)) ));
return image;
}
diff --git a/imagemagickwrapper.h b/imagemagickwrapper.h
index a5cc482..5f9901e 100644
--- a/imagemagickwrapper.h
+++ b/imagemagickwrapper.h
@@ -17,7 +17,7 @@ public:
protected:
Image buffer;
Color Argb2Color(tColor col);
- cImage *CreateImage(void);
+ cImage *CreateImage(int width, int height, bool preserveAspect = true);
cImage CreateImageCopy(void);
bool LoadImage(std::string FileName, std::string Path, std::string Extension);
bool LoadImage(const char *fullpath);
diff --git a/imagescaler.c b/imagescaler.c
new file mode 100644
index 0000000..cebe912
--- /dev/null
+++ b/imagescaler.c
@@ -0,0 +1,149 @@
+
+#include "imagescaler.h"
+
+#include <cstdlib>
+#include <cmath>
+
+ImageScaler::ImageScaler() :
+ m_memory(NULL),
+ m_hor_filters(NULL),
+ m_ver_filters(NULL),
+ m_buffer(NULL),
+ m_dst_image(NULL),
+ m_dst_stride(0),
+ m_dst_width(0),
+ m_dst_height(0),
+ m_src_width(0),
+ m_src_height(0),
+ m_src_x(0),
+ m_src_y(0),
+ m_dst_x(0),
+ m_dst_y(0) {
+}
+
+ImageScaler::~ImageScaler() {
+ if ( m_memory ) free( m_memory );
+}
+
+// sin(x)/(x)
+static float sincf( float x ) {
+ if ( fabsf(x) < 0.05f ) return 1.0f - (1.0f/6.0f)*x*x; // taylor series approximation to avoid 0/0
+ return sin(x)/x;
+}
+
+static void CalculateFilters( ImageScaler::Filter *filters, int dst_size, int src_size ) {
+ const float fc = dst_size >= src_size ? 1.0f : ((float) dst_size)/((float) src_size);
+
+ for (int i = 0; i < dst_size; i++) {
+ const int d = 2*dst_size; // sample position denominator
+ const int e = (2*i+1) * src_size - dst_size; // sample position enumerator
+ int offset = e / d; // truncated sample position
+ const float sub_offset = ((float) (e - offset*d)) / ((float) d); // exact sample position is (float) e/d = offset + sub_offset
+
+ // calculate filter coefficients
+ float h[4];
+ for (int j=0; j<4; j++) {
+ const float t = 3.14159265359f * (sub_offset+(1-j));
+ h[j] = sincf( fc * t ) * cosf( 0.25f * t ); // sinc-lowpass and cos-window
+ }
+
+ // ensure that filter does not reach out off image bounds:
+ while ( offset < 1 ) {
+ h[0] += h[1];
+ h[1] = h[2];
+ h[2] = h[3];
+ h[3] = 0.0f;
+ offset++;
+ }
+
+ while ( offset+3 > src_size ) {
+ h[3] += h[2];
+ h[2] = h[1];
+ h[1] = h[0];
+ h[0] = 0.0f;
+ offset--;
+ }
+
+ // coefficients are normalized to sum up to 2048
+ const float norm = 2048.0f / ( h[0] + h[1] + h[2] + h[3] );
+
+ offset--; // offset of fist used pixel
+
+ filters[i].m_offset = offset + 4; // store offset of first unused pixel
+
+ for (int j=0; j<4; j++) {
+ const float t = norm * h[j];
+ filters[i].m_coeff[(offset+j) & 3] = (int) ((t > 0.0f) ? (t+0.5f) : (t-0.5f)); // consider ring buffer index permutations
+ }
+ }
+
+ // set end marker
+ filters[dst_size].m_offset = (unsigned) -1;
+
+}
+
+void ImageScaler::SetImageParameters( unsigned *dst_image, unsigned dst_stride, unsigned dst_width, unsigned dst_height, unsigned src_width, unsigned src_height ) {
+ m_src_x = 0;
+ m_src_y = 0;
+ m_dst_x = 0;
+ m_dst_y = 0;
+
+ m_dst_image = dst_image;
+ m_dst_stride = dst_stride;
+
+ // if image dimensions do not change we can keep the old filter coefficients
+ if ( (src_width == m_src_width) && (src_height == m_src_height) && (dst_width == m_dst_width) && (dst_height == m_dst_height) ) return;
+
+ m_dst_width = dst_width;
+ m_dst_height = dst_height;
+ m_src_width = src_width;
+ m_src_height = src_height;
+
+ if ( m_memory ) free( m_memory );
+
+ const unsigned hor_filters_size = (m_dst_width + 1) * sizeof(Filter); // reserve one extra position for end marker
+ const unsigned ver_filters_size = (m_dst_height + 1) * sizeof(Filter);
+ const unsigned buffer_size = 4 * m_dst_width * sizeof(TmpPixel);
+
+ char *p = (char *) malloc( hor_filters_size + ver_filters_size + buffer_size );
+
+ m_memory = p;
+
+ m_hor_filters = (Filter *) p; p += hor_filters_size;
+ m_ver_filters = (Filter *) p; p += ver_filters_size;
+ m_buffer = (TmpPixel *) p;
+
+ CalculateFilters( m_hor_filters, m_dst_width , m_src_width );
+ CalculateFilters( m_ver_filters, m_dst_height, m_src_height );
+}
+
+// shift range to 0..255 and clamp overflows
+static unsigned shift_clamp( int x ) {
+ x = ( x + (1<<21) ) >> 22;
+ if ( x < 0 ) return 0;
+ if ( x > 255 ) return 255;
+ return x;
+}
+
+void ImageScaler::NextSourceLine() {
+ m_dst_x = 0;
+ m_src_x = 0;
+ m_src_y++;
+
+ while ( m_ver_filters[m_dst_y].m_offset == m_src_y ) {
+ const int h0 = m_ver_filters[m_dst_y].m_coeff[0];
+ const int h1 = m_ver_filters[m_dst_y].m_coeff[1];
+ const int h2 = m_ver_filters[m_dst_y].m_coeff[2];
+ const int h3 = m_ver_filters[m_dst_y].m_coeff[3];
+ const TmpPixel *src = m_buffer;
+ unsigned *dst = m_dst_image + m_dst_stride * m_dst_y;
+
+ for (unsigned i=0; i<m_dst_width; i++) {
+ const ImageScaler::TmpPixel t( src[0]*h0 + src[1]*h1 + src[2]*h2 + src[3]*h3 );
+ src += 4;
+ dst[i] = shift_clamp(t[0]) | (shift_clamp(t[1])<<8) | (shift_clamp(t[2])<<16) | (shift_clamp(t[3])<<24);
+ }
+
+ m_dst_y++;
+ }
+}
diff --git a/imagescaler.h b/imagescaler.h
new file mode 100644
index 0000000..f2de6ba
--- /dev/null
+++ b/imagescaler.h
@@ -0,0 +1,97 @@
+#ifndef _ImageScaler_h
+#define _ImageScaler_h
+
+/*!
+ * this class scales images consisting of 4 components (RGBA)
+ * to an arbitrary size using a 4-tap filter
+ */
+class ImageScaler {
+public:
+
+ struct Filter {
+ unsigned m_offset;
+ short m_coeff[4];
+ };
+
+ ImageScaler();
+ ~ImageScaler();
+
+ //! set destination image and source image size
+ void SetImageParameters( unsigned *dst_image, unsigned dst_stride, unsigned dst_width, unsigned dst_height, unsigned src_width, unsigned src_height );
+
+ /*! process one pixel of source image; destination image is written while input is processed
+ * SetImageParameters() must be called first
+ */
+ void PutSourcePixel( unsigned char c0, unsigned char c1, unsigned char c2, unsigned char c3 ) {
+ m_hbuf[ (m_src_x++) & 3 ].Set( c0, c1, c2, c3 );
+
+ TmpPixel *bp = m_buffer + 4 * m_dst_x + (m_src_y & 3);
+ const Filter *fh;
+
+ while ( (fh=m_hor_filters+m_dst_x)->m_offset == m_src_x ) {
+ *bp = m_hbuf[0]*fh->m_coeff[0] + m_hbuf[1]*fh->m_coeff[1] + m_hbuf[2]*fh->m_coeff[2] + m_hbuf[3]*fh->m_coeff[3];
+ m_dst_x++;
+ bp += 4;
+ }
+
+ if ( m_src_x == m_src_width ) NextSourceLine();
+ }
+
+private:
+
+ //! temporary image pixel class - a 4-element integer vector
+ class TmpPixel {
+ public:
+ TmpPixel() {
+ }
+
+ TmpPixel( int c0, int c1, int c2, int c3 ) {
+ Set(c0,c1,c2,c3);
+ }
+
+ void Set( int c0, int c1, int c2, int c3 ) {
+ m_comp[0] = c0;
+ m_comp[1] = c1;
+ m_comp[2] = c2;
+ m_comp[3] = c3;
+ }
+
+ TmpPixel operator*( int s ) const {
+ return TmpPixel( m_comp[0]*s, m_comp[1]*s, m_comp[2]*s, m_comp[3]*s );
+ }
+
+ TmpPixel operator+( const TmpPixel &x ) const {
+ return TmpPixel( m_comp[0] + x[0], m_comp[1] + x[1], m_comp[2] + x[2], m_comp[3] + x[3] );
+ }
+
+ // return component i=[0..3] - No range check!
+ int operator[](unsigned i) const {
+ return m_comp[i];
+ }
+
+ private:
+ int m_comp[4];
+ };
+
+ //! this is called whenever one input line is processed completely
+ void NextSourceLine();
+
+ TmpPixel m_hbuf[4]; //! ring buffer for 4 input pixels
+ char *m_memory; //! buffer container
+ Filter *m_hor_filters; //! buffer for horizontal filters (one for each output image column)
+ Filter *m_ver_filters; //! buffer for vertical filters (one for each output image row)
+ TmpPixel *m_buffer; //! buffer contains 4 horizontally filtered input lines, multiplexed
+ unsigned *m_dst_image; //! pointer to destination image
+ unsigned m_dst_stride; //! destination image stride
+ unsigned m_dst_width; //! destination image width
+ unsigned m_dst_height; //! destination image height
+ unsigned m_src_width; //! source image width
+ unsigned m_src_height; //! source image height
+ unsigned m_src_x; //! x position of next source image pixel
+ unsigned m_src_y; //! y position of source image line currently beeing processed
+ unsigned m_dst_x; //! x position of next destination image pixel
+ unsigned m_dst_y; //! x position of next destination image line
+};
+
+#endif // _ImageScaler_h
+