1 #include <iostream>
2 #include <cstring>
3 
4 #include "../support/debug.h"
5 
6 #include "imagesource.h"
7 #include "imagesource_jpeg.h"
8 #include "imagesource_tiff.h"
9 #include "imagesource_bmp.h"
10 #include "imagesource_pnm.h"
11 #include "imagesource_gdkpixbuf.h"
12 #include "imagesource_gs.h"
13 
14 #include "imagesource_scale.h"
15 #include "imagesource_downsample.h"
16 #include "imagesource_bilinear.h"
17 #include "imagesource_lanczossinc.h"
18 
19 #include "imagesource_util.h"
20 
21 
22 #include "../config.h"
23 
24 #include "gettext.h"
25 #define _(x) gettext(x)
26 #define N_(x) gettext_noop(x)
27 
28 using namespace std;
29 
30 static const IS_ScalingQualityDescription Descriptions[]=
31 {
32 	{N_("Automatic"),N_("Selects a scaling algorithm based on scale factor")},
33 	{N_("Fast"),N_("Very fast scaling with no interpolation")},
34 	{N_("Bilinear"),N_("An interpolation filter with mediocre speed and quality")},
35 	{N_("LanczosSinc"),N_("Slow but very high quality interpolation filter")},
36 	{NULL,NULL},
37 	{N_("Downsample"),N_("High quality filter for reductions only")}
38 };
39 
40 
findextension(const char * filename)41 static const char *findextension(const char *filename)
42 {
43 	int t=strlen(filename)-1;
44 	int c;
45 	for(c=t;c>0;--c)
46 	{
47 		if(filename[c]=='.')
48 			return(filename+c);
49 	}
50 	return(filename);
51 }
52 
53 
ISLoadImage_core(const char * filename)54 static ImageSource *ISLoadImage_core(const char *filename)
55 {
56 	const char *ext=findextension(filename);
57 	try
58 	{
59 		Debug[COMMENT] << "Loading filename: " << filename << endl;
60 		Debug[COMMENT] << "Extension: " << ext << endl;
61 		if(strncasecmp(ext,".JPG",4)==0)
62 			return(new ImageSource_JPEG(filename));
63 		else if(strncasecmp(ext,".JPEG",5)==0)
64 			return(new ImageSource_JPEG(filename));
65 		else if(strncasecmp(ext,".JFIF",5)==0)
66 			return(new ImageSource_JPEG(filename));
67 		else if(strncasecmp(ext,".BMP",4)==0)
68 			return(new ImageSource_BMP(filename));
69 		else if(strncasecmp(ext,".TIF",4)==0)
70 			return(new ImageSource_TIFF(filename));
71 		else if(strncasecmp(ext,".TIFF",5)==0)
72 			return(new ImageSource_TIFF(filename));
73 #if defined HAVE_LIBPNM || defined HAVE_LIBNETPBM
74 		else if(strncasecmp(ext,".PPM",4)==0)
75 			return(new ImageSource_PNM(filename));
76 		else if(strncasecmp(ext,".PGM",4)==0)
77 			return(new ImageSource_PNM(filename));
78 		else if(strncasecmp(ext,".PBM",4)==0)
79 			return(new ImageSource_PNM(filename));
80 		else if(strncasecmp(ext,".PNM",4)==0)
81 			return(new ImageSource_PNM(filename));
82 #endif
83 		else if(strncasecmp(ext,".PS",4)==0)
84 			return(new ImageSource_GS(filename,IMAGESOURCE_GS_DEFAULT_RESOLUTION));
85 		else if(strncasecmp(ext,".PDF",4)==0)
86 			return(new ImageSource_GS(filename,IMAGESOURCE_GS_DEFAULT_RESOLUTION));
87 	}
88 	catch(const char *err)
89 	{
90 		Debug[WARN] << "Attempt to load " << filename << " failed" << endl;
91 		Debug[WARN] << "(" << err << ")" << endl;
92 		Debug[WARN] << "- falling back to GdkPixbuf loader" << endl;
93 	}
94 	return(new ImageSource_GdkPixbuf(filename));
95 }
96 
97 
98 // Enforce sane defaults for resolution if the loader leaves it at zero.
99 
ISLoadImage(const char * filename)100 ImageSource *ISLoadImage(const char *filename)
101 {
102 	ImageSource *result=ISLoadImage_core(filename);
103 	if(result)
104 	{
105 		if(result->xres==0)
106 			result->xres=72;
107 		if(result->yres==0)
108 			result->yres=72;
109 	}
110 	return(result);
111 }
112 
113 
ISScaleImageByResolution(ImageSource * source,double xres,double yres,IS_ScalingQuality quality)114 ImageSource *ISScaleImageByResolution(ImageSource *source,double xres,double yres,IS_ScalingQuality quality)
115 {
116 	ImageSource *result=NULL;
117 	int w=int((source->width*xres)/source->xres);
118 	int h=int((source->height*yres)/source->yres);
119 
120 	result=ISScaleImageBySize(source,w,h,quality);
121 
122 	if(result)
123 		result->SetResolution(xres,yres);
124 
125 	return(result);
126 }
127 
128 
ChooseScaling(int src,int dst,IS_ScalingQuality quality)129 static IS_ScalingQuality ChooseScaling(int src,int dst,IS_ScalingQuality quality)
130 {
131 	if(quality==IS_SCALING_AUTOMATIC)
132 	{
133 		double f=dst;  f/=src;
134 		// Use LanczosSinc for scaling >250%,
135 		// Bilinear for 250% - 100%, and
136 		// nearest neighbour for scaling <=100%
137 		if(f>2.5)
138 			quality=IS_SCALING_LANCZOSSINC;
139 		else if(f>1.0)
140 			quality=IS_SCALING_BILINEAR;
141 		else
142 			quality=IS_SCALING_DOWNSAMPLE;
143 	}
144 	else if ((quality!=IS_SCALING_NEARESTNEIGHBOUR) && (dst<src))
145 	{
146 		// If we're downsampling, and the quality hasn't been explicitly set to
147 		// nearest neighbour, we use the downsample method;
148 		quality=IS_SCALING_DOWNSAMPLE;
149 	}
150 
151 	return(quality);
152 }
153 
154 
ISScaleImageBySize(ImageSource * source,int width,int height,IS_ScalingQuality quality)155 ImageSource *ISScaleImageBySize(ImageSource *source,int width,int height,IS_ScalingQuality quality)
156 {
157 	IS_ScalingQuality hscale=ChooseScaling(source->width,width,quality);
158 	IS_ScalingQuality vscale=ChooseScaling(source->height,height,quality);
159 
160 	switch(hscale)
161 	{
162 		default:
163 		case IS_SCALING_NEARESTNEIGHBOUR:
164 			source=new ImageSource_HScale(source,width);
165 			break;
166 		case IS_SCALING_BILINEAR:
167 			source=new ImageSource_HBilinear(source,width);
168 			break;
169 		case IS_SCALING_LANCZOSSINC:
170 			// We could perform a slight downsampling before the LanczosSinc scaling
171 			// to reduce ringing artifacts, at the expense of a small amount
172 			// of sharpness
173 //			source=new ImageSource_HDownsample(source,(source->width*95)/100);
174 			source=new ImageSource_HLanczosSinc(source,width);
175 			break;
176 		case IS_SCALING_DOWNSAMPLE:
177 			source=new ImageSource_HDownsample(source,width);
178 			break;
179 	}
180 
181 	switch(vscale)
182 	{
183 		default:
184 		case IS_SCALING_NEARESTNEIGHBOUR:
185 			source=new ImageSource_VScale(source,height);
186 			break;
187 		case IS_SCALING_BILINEAR:
188 			source=new ImageSource_VBilinear(source,height);
189 			break;
190 		case IS_SCALING_LANCZOSSINC:
191 			// We could perform a slight downsampling before the LanczosSinc scaling
192 			// to reduce ringing artifacts, at the expense of a small amount
193 			// of sharpness
194 //			source=new ImageSource_VDownsample(source,(source->height*95)/100);
195 			source=new ImageSource_VLanczosSinc(source,height);
196 			break;
197 		case IS_SCALING_DOWNSAMPLE:
198 			source=new ImageSource_VDownsample(source,height);
199 			break;
200 	}
201 
202 #if 0
203 	// don't use an expensive scaling function if the image is being reduced...
204 	if((width<source->width) && (height<source->height))
205 	{
206 		switch(quality)
207 		{
208 			case IS_SCALING_NEARESTNEIGHBOUR:
209 				Debug[TRACE] << "Image is being shrunk - using Nearest Neighbour scaling" << endl;
210 				quality=IS_SCALING_NEARESTNEIGHBOUR;
211 				break;
212 			default:
213 				Debug[TRACE] << "Using Downsample filter..." << endl;
214 				quality=IS_SCALING_DOWNSAMPLE;
215 				break;
216 		}
217 	}
218 
219 	switch(quality)
220 	{
221 		case IS_SCALING_NEARESTNEIGHBOUR:
222 		case IS_SCALING_LANCZOSSINC:
223 		case IS_SCALING_BILINEAR:
224 		case IS_SCALING_DOWNSAMPLE:
225 			break;
226 		case IS_SCALING_AUTOMATIC:
227 		default:
228 			{
229 				double xf=width; xf/=source->width;
230 				double yf=height; yf/=source->height;
231 				double f=xf;
232 				if(yf>f) f=yf;
233 				// Use LanczosSinc for scaling >250%,
234 				// Bilinear for 250% - 100%, and
235 				// nearest neighbour for scaling <=100%
236 				if(f>2.5)
237 					quality=IS_SCALING_LANCZOSSINC;
238 				else if(f>1.0)
239 					quality=IS_SCALING_BILINEAR;
240 				else
241 					quality=IS_SCALING_NEARESTNEIGHBOUR;
242 			}
243 	}
244 
245 	switch(quality)
246 	{
247 		default:
248 		case IS_SCALING_NEARESTNEIGHBOUR:
249 			result=new ImageSource_Scale(source,width,height);
250 			break;
251 		case IS_SCALING_BILINEAR:
252 			result=new ImageSource_Bilinear(source,width,height);
253 			break;
254 		case IS_SCALING_LANCZOSSINC:
255 			result=new ImageSource_LanczosSinc(source,width,height);
256 			break;
257 		case IS_SCALING_DOWNSAMPLE:
258 			result=new ImageSource_Downsample(source,width,height);
259 			break;
260 	}
261 #endif
262 	return(source);
263 }
264 
265 
DescribeScalingQuality(IS_ScalingQuality quality)266 const IS_ScalingQualityDescription *DescribeScalingQuality(IS_ScalingQuality quality)
267 {
268 	if(quality<IS_SCALING_MAX)
269 		return(&Descriptions[quality]);
270 	else
271 		return(NULL);
272 }
273