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