1 /*
2  * imagesource_lanczossinc.cpp - Interpolated scaling filter
3  * Implements Sinc interpolation with Lanczos Window:
4  * sin(pi*x)/(pi*x)*sample*sin(pi*x/windowsize)/(pi*x/windowsize)
5  *
6  * Supports Greyscale, RGB and CMYK data
7  * Doesn't (yet) support random access
8  *
9  * Copyright (c) 2004-2008 by Alastair M. Robinson
10  * Distributed under the terms of the GNU General Public License -
11  * see the file named "COPYING" for more details.
12  *
13  */
14 
15 #include <iostream>
16 #include <stdlib.h>
17 #include <string.h>
18 #include <math.h>
19 
20 #include "imagesource_lanczossinc.h"
21 
22 using namespace std;
23 
24 
25 // 2D scaling is implemented as a chaining of a horizontal, then a vertical scaling;
26 
27 
~ImageSource_LanczosSinc()28 ImageSource_LanczosSinc::~ImageSource_LanczosSinc()
29 {
30 	if(source)
31 		delete source;
32 }
33 
34 
GetRow(int row)35 ISDataType *ImageSource_LanczosSinc::GetRow(int row)
36 {
37 	return(source->GetRow(row));
38 }
39 
40 
ImageSource_LanczosSinc(struct ImageSource * source,int width,int height,int window)41 ImageSource_LanczosSinc::ImageSource_LanczosSinc(struct ImageSource *source,int width,int height,int window)
42 	: ImageSource(source), source(source)
43 {
44 	this->source=new ImageSource_HLanczosSinc(this->source,width,window);
45 	this->source=new ImageSource_VLanczosSinc(this->source,height,window);
46 	xres=this->source->xres;
47 	yres=this->source->yres;
48 	this->randomaccess=this->source->randomaccess;
49 	this->width=width;
50 	this->height=height;
51 }
52 
53 
54 // Sinc function.
55 
56 #ifndef M_PI
57 #define M_PI    3.14159265358979323846
58 #endif
59 
sinc(double x)60 static double sinc(double x)
61 {
62 	x*=M_PI;
63 	if(x==0.0)
64 		return(1.0);
65 	else
66 		return(sin(x)/x);
67 }
68 
69 
70 // The row cache is just a simplistic ring-buffer type cache which handles
71 // the details of tracking several rows of "support" data.
72 // The temporary data does have to be float - or at least wider than ISDataType
73 // and must be clamped since the ringing artifiacts inherent in Lanczos Sinc
74 // can push values out of range.
75 
76 class ISLanczosSinc_RowCache
77 {
78 	public:
79 	ISLanczosSinc_RowCache(ImageSource_VLanczosSinc *source);
80 	~ISLanczosSinc_RowCache();
81 	double *GetRow(int row);
82 	double *GetCacheRow(int row);
83 	private:
84 	ImageSource_VLanczosSinc *source;
85 	double *cache;
86 	double *rowbuffer;
87 	int currentrow;
88 };
89 
90 
~ISLanczosSinc_RowCache()91 ISLanczosSinc_RowCache::~ISLanczosSinc_RowCache()
92 {
93 	if(cache)
94 		free(cache);
95 	if(rowbuffer)
96 		free(rowbuffer);
97 }
98 
99 
ISLanczosSinc_RowCache(ImageSource_VLanczosSinc * source)100 ISLanczosSinc_RowCache::ISLanczosSinc_RowCache(ImageSource_VLanczosSinc *source)
101 	: source(source), cache(NULL), rowbuffer(NULL), currentrow(-1)
102 {
103 	cache=(double *)malloc(sizeof(double)*source->samplesperpixel*source->width*source->support);
104 	rowbuffer=(double *)malloc(sizeof(double)*source->samplesperpixel*source->width);
105 }
106 
107 
GetRow(int row)108 double *ISLanczosSinc_RowCache::GetRow(int row)
109 {
110 	for(int i=0;i<source->width*source->samplesperpixel;++i)
111 		rowbuffer[i]=0.0;
112 
113 	for(int i=0;i<source->support;++i)
114 	{
115 		int p=i-source->windowsize;
116 		int sr=(row*source->source->height)/source->height;
117 		double *src=GetCacheRow(sr+p);
118 		double f=source->coeff[row*source->support+i];
119 		for(int x=0;x<source->width*source->samplesperpixel;++x)
120 		{
121 			rowbuffer[x]+=f*src[x];
122 		}
123 	}
124 	return(rowbuffer);
125 }
126 
127 
GetCacheRow(int row)128 double *ISLanczosSinc_RowCache::GetCacheRow(int row)
129 {
130 	if(row<0)
131 		row=0;
132 	if(row>=source->source->height)
133 		row=source->source->height-1;
134 	int crow=row%source->support;
135 	{
136 		double *rowptr=cache+crow*source->samplesperpixel*source->width;
137 		if(row>currentrow)
138 		{
139 			currentrow=row;
140 			ISDataType *src=source->source->GetRow(row);
141 			for(int i=0;i<source->width*source->samplesperpixel;++i)
142 			{
143 				rowptr[i]=src[i];
144 			}
145 		}
146 		return(rowptr);
147 	}
148 }
149 
150 
151 // Vertical scaling
152 
153 
~ImageSource_VLanczosSinc()154 ImageSource_VLanczosSinc::~ImageSource_VLanczosSinc()
155 {
156 	if(cache)
157 		delete cache;
158 	if(source)
159 		delete source;
160 	if(coeff)
161 		free(coeff);
162 }
163 
164 
GetRow(int row)165 ISDataType *ImageSource_VLanczosSinc::GetRow(int row)
166 {
167 	int i;
168 
169 	if(row==currentrow)
170 		return(rowbuffer);
171 
172 	double *srcdata=cache->GetRow(row);
173 
174 	for(i=0;i<width*samplesperpixel;++i)
175 	{
176 		double s=srcdata[i];
177 		if(s<0.0) s=0.0;
178 		if(s>IS_SAMPLEMAX) s=IS_SAMPLEMAX;
179 		rowbuffer[i]=int(s);
180 	}
181 
182 	currentrow=row;
183 
184 	return(rowbuffer);
185 }
186 
187 
188 // Since Lanczos Sinc is a relatively expensive algorithm to compute in realtime
189 // and the row / column coefficients are constant we precompute them here.
190 
PreCalc()191 void ImageSource_VLanczosSinc::PreCalc()
192 {
193 	for(int y=0;y<height;++y)
194 	{
195 		double c=y*source->height; c/=height;
196 		int ci=int(c);
197 		double f=c-ci;
198 		for(int i=0; i<support; ++i)
199 		{
200 			int p=i-windowsize;
201 			double v=f-p;
202 			coeff[support*y+i]=sinc(v)*sinc(v/windowsize);
203 		}
204 	}
205 }
206 
207 
ImageSource_VLanczosSinc(struct ImageSource * source,int height,int windowsize)208 ImageSource_VLanczosSinc::ImageSource_VLanczosSinc(struct ImageSource *source,int height,int windowsize)
209 	: ImageSource(source), source(source), windowsize(windowsize)
210 {
211 	this->height=height;
212 	yres=(source->yres*height); yres/=source->height;
213 
214 	support=windowsize*2+1;
215 	coeff=(double *)malloc(sizeof(double)*height*support);
216 	PreCalc();
217 	cache=new ISLanczosSinc_RowCache(this);
218 	MakeRowBuffer();
219 	randomaccess=false;
220 }
221 
222 
223 // Horizontal scaling
224 
225 
GetRow(int row)226 ISDataType *ImageSource_HLanczosSinc::GetRow(int row)
227 {
228 	if(row==currentrow)
229 		return(rowbuffer);
230 
231 	ISDataType *src=source->GetRow(row);
232 	for(int x=0;x<width;++x)
233 	{
234 		int sx=(x*source->width)/width;
235 		for(int s=0;s<samplesperpixel;++s)
236 		{
237 			double a=0.0;
238 			for(int p=0;p<support;++p)
239 			{
240 				int lsx=sx+p-windowsize;
241 				if(lsx<0) lsx=0;
242 				if(lsx>=source->width) lsx=source->width-1;
243 
244 				a+=src[lsx*samplesperpixel+s]*coeff[x*support+p];
245 			}
246 			if(a<0.0) a=0.0;
247 			if(a>IS_SAMPLEMAX) a=IS_SAMPLEMAX;
248 			rowbuffer[x*samplesperpixel+s]=a;
249 		}
250 	}
251 
252 	currentrow=row;
253 
254 	return(rowbuffer);
255 }
256 
257 
258 // Since Lanczos Sinc is a relatively expensive algorithm to compute in realtime
259 // and the row / column coefficients are constant we precompute them here.
260 
PreCalc()261 void ImageSource_HLanczosSinc::PreCalc()
262 {
263 	for(int x=0;x<width;++x)
264 	{
265 		double c=x*source->width; c/=width;
266 		int ci=int(c);
267 		double f=c-ci;
268 		for(int i=0; i<support; ++i)
269 		{
270 			int p=i-windowsize;
271 			double v=f-p;
272 			coeff[support*x+i]=sinc(v)*sinc(v/windowsize);
273 		}
274 	}
275 }
276 
277 
ImageSource_HLanczosSinc(struct ImageSource * source,int width,int windowsize)278 ImageSource_HLanczosSinc::ImageSource_HLanczosSinc(struct ImageSource *source,int width,int windowsize)
279 	: ImageSource(source), source(source), windowsize(windowsize)
280 {
281 	this->width=width;
282 	xres=(source->xres*width); xres/=source->width;
283 
284 	support=windowsize*2+1;
285 	coeff=(double *)malloc(sizeof(double)*width*support);
286 	PreCalc();
287 	MakeRowBuffer();
288 }
289 
290 
~ImageSource_HLanczosSinc()291 ImageSource_HLanczosSinc::~ImageSource_HLanczosSinc()
292 {
293 	if(source)
294 		delete source;
295 	if(coeff)
296 		free(coeff);
297 }
298 
299 
300 
301