1 #ifdef _MSC_VER
2 #define NO_XSLOCKS // Needed to avoid PerlProc_setjmp/PerlProc_longjmp unresolved symbols
3 #endif
4 
5 // On Debian, pngconf.h might complain about setjmp.h being loaded before PNG
6 // so we have to load png.h first
7 #ifdef HAVE_PNG
8 #include <png.h>
9 #endif
10 
11 #include "EXTERN.h"
12 #include "perl.h"
13 #include "XSUB.h"
14 
15 #include "ppport.h"
16 
17 #include "common.c"
18 #include "image.c"
19 
20 MODULE = Image::Scale		PACKAGE = Image::Scale
21 
22 PROTOTYPES: ENABLE
23 
24 void
__init(HV * self)25 __init(HV *self)
26 PPCODE:
27 {
28   SV *pv = NEWSV(0, sizeof(image));
29   image *im = (image *)SvPVX(pv);
30 
31   SvPOK_only(pv);
32 
33   if ( !image_init(self, im) ) {
34     // Return undef on any errors during header reading
35     SvREFCNT_dec(pv);
36     XSRETURN_UNDEF;
37   }
38 
39   XPUSHs( sv_2mortal( sv_bless(
40     newRV_noinc(pv),
41     gv_stashpv("Image::Scale::XS", 1)
42   ) ) );
43 }
44 
45 int
width(HV * self)46 width(HV *self)
47 CODE:
48 {
49   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
50 
51   RETVAL = im->width;
52 }
53 OUTPUT:
54   RETVAL
55 
56 int
height(HV * self)57 height(HV *self)
58 CODE:
59 {
60   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
61 
62   RETVAL = im->height;
63 }
64 OUTPUT:
65   RETVAL
66 
67 int
resized_width(HV * self)68 resized_width(HV *self)
69 CODE:
70 {
71   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
72 
73   RETVAL = im->target_width;
74 }
75 OUTPUT:
76   RETVAL
77 
78 int
resized_height(HV * self)79 resized_height(HV *self)
80 CODE:
81 {
82   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
83 
84   RETVAL = im->target_height;
85 }
86 OUTPUT:
87   RETVAL
88 
89 int
resize(HV * self,HV * opts)90 resize(HV *self, HV *opts)
91 CODE:
92 {
93   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
94 
95   // Reset options if resize is being called multiple times
96   if (im->target_width) {
97     im->target_width  = 0;
98     im->target_height = 0;
99     im->keep_aspect   = 0;
100     im->orientation   = im->orientation_orig;
101     im->bgcolor       = 0;
102     im->memory_limit  = 0;
103     im->resize_type   = IMAGE_SCALE_TYPE_GD;
104     im->filter        = 0;
105   }
106 
107   if (my_hv_exists(opts, "width"))
108     im->target_width = SvIV(*(my_hv_fetch(opts, "width")));
109 
110   if (my_hv_exists(opts, "height"))
111     im->target_height = SvIV(*(my_hv_fetch(opts, "height")));
112 
113   if (!im->target_width && !im->target_height) {
114     croak("Image::Scale->resize requires at least one of height or width");
115   }
116 
117   if (my_hv_exists(opts, "keep_aspect"))
118     im->keep_aspect = SvIV(*(my_hv_fetch(opts, "keep_aspect")));
119 
120   if (my_hv_exists(opts, "ignore_exif")) {
121     if (SvIV(*(my_hv_fetch(opts, "ignore_exif"))) != 0)
122       im->orientation = ORIENTATION_NORMAL;
123   }
124 
125   if (my_hv_exists(opts, "bgcolor"))
126     im->bgcolor = SvIV(*(my_hv_fetch(opts, "bgcolor"))) << 8 | 0xFF;
127 
128   if (my_hv_exists(opts, "memory_limit"))
129     im->memory_limit = SvIV(*(my_hv_fetch(opts, "memory_limit")));
130 
131   if (my_hv_exists(opts, "type"))
132     im->resize_type = SvIV(*(my_hv_fetch(opts, "type")));
133 
134   if (my_hv_exists(opts, "filter")) {
135     char *filterstr = SvPVX(*(my_hv_fetch(opts, "filter")));
136     if (strEQ("Point", filterstr))
137       im->filter = PointFilter;
138     else if (strEQ("Box", filterstr))
139       im->filter = BoxFilter;
140     else if (strEQ("Triangle", filterstr))
141       im->filter = TriangleFilter;
142     else if (strEQ("Hermite", filterstr))
143       im->filter = HermiteFilter;
144     else if (strEQ("Hanning", filterstr))
145       im->filter = HanningFilter;
146     else if (strEQ("Hamming", filterstr))
147       im->filter = HammingFilter;
148     else if (strEQ("Blackman", filterstr))
149       im->filter = BlackmanFilter;
150     else if (strEQ("Gaussian", filterstr))
151       im->filter = GaussianFilter;
152     else if (strEQ("Quadratic", filterstr))
153       im->filter = QuadraticFilter;
154     else if (strEQ("Cubic", filterstr))
155       im->filter = CubicFilter;
156     else if (strEQ("Catrom", filterstr))
157       im->filter = CatromFilter;
158     else if (strEQ("Mitchell", filterstr))
159       im->filter = MitchellFilter;
160     else if (strEQ("Lanczos", filterstr))
161       im->filter = LanczosFilter;
162     else if (strEQ("Bessel", filterstr))
163       im->filter = BesselFilter;
164     else if (strEQ("Sinc", filterstr))
165       im->filter = SincFilter;
166   }
167 
168   // If the image will be rotated 90 degrees, swap the target values
169   if (im->orientation >= 5) {
170     if (!im->target_height) {
171       // Only width was specified, but this will actually be the target height
172       im->target_height = im->target_width;
173       im->target_width = 0;
174     }
175     else if (!im->target_width) {
176       // Only height was specified, but this will actually be the target width
177       im->target_width = im->target_height;
178       im->target_height = 0;
179     }
180   }
181 
182   if (!im->target_height) {
183     // Only width was specified
184     im->target_height = (int)((float)im->height / im->width * im->target_width);
185     if (im->target_height < 1)
186       im->target_height = 1;
187   }
188   else if (!im->target_width) {
189     // Only height was specified
190     im->target_width = (int)((float)im->width / im->height * im->target_height);
191     if (im->target_width < 1)
192       im->target_width = 1;
193   }
194 
195   DEBUG_TRACE("Resizing from %d x %d -> %d x %d\n", im->width, im->height, im->target_width, im->target_height);
196 
197   RETVAL = image_resize(im);
198 }
199 OUTPUT:
200   RETVAL
201 
202 #ifdef HAVE_JPEG
203 void
save_jpeg(HV * self,SV * path,...)204 save_jpeg(HV *self, SV *path, ...)
205 CODE:
206 {
207   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
208   int quality = DEFAULT_JPEG_QUALITY;
209 
210   if ( !SvPOK(path) ) {
211     croak("Image::Scale->save_jpeg requires a path");
212   }
213 
214   if (items == 3 && SvOK(ST(2))) {
215     quality = SvIV(ST(2));
216   }
217 
218   image_jpeg_save(im, SvPVX(path), quality);
219 }
220 
221 SV *
as_jpeg(HV * self,...)222 as_jpeg(HV *self, ...)
223 CODE:
224 {
225   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
226   int quality = DEFAULT_JPEG_QUALITY;
227 
228   if (items == 2 && SvOK(ST(1))) {
229     quality = SvIV(ST(1));
230   }
231 
232   RETVAL = newSVpvn("", 0);
233 
234   image_jpeg_to_sv(im, quality, RETVAL);
235 }
236 OUTPUT:
237   RETVAL
238 
239 #endif
240 
241 #ifdef HAVE_PNG
242 void
save_png(HV * self,SV * path)243 save_png(HV *self, SV *path)
244 CODE:
245 {
246   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
247 
248   if ( !SvPOK(path) ) {
249     croak("Image::Scale->save_jpeg requires a path");
250   }
251 
252   image_png_save(im, SvPVX(path));
253 }
254 
255 SV *
as_png(HV * self)256 as_png(HV *self)
257 CODE:
258 {
259   image *im = (image *)SvPVX(SvRV(*(my_hv_fetch(self, "_image"))));
260 
261   RETVAL = newSVpvn("", 0);
262 
263   image_png_to_sv(im, RETVAL);
264 }
265 OUTPUT:
266   RETVAL
267 
268 #endif
269 
270 void
__cleanup(HV * self,image * im)271 __cleanup(HV *self, image *im)
272 CODE:
273 {
274   image_finish(im);
275 }
276 
277 SV *
jpeg_version(void)278 jpeg_version(void)
279 CODE:
280 {
281 #ifdef JPEG_VERSION
282   RETVAL = newSVpv( STRINGIFY(JPEG_VERSION), 0 );
283 #else
284   RETVAL = &PL_sv_undef;
285 #endif
286 }
287 OUTPUT:
288   RETVAL
289 
290 SV *
png_version(void)291 png_version(void)
292 CODE:
293 {
294 #ifdef PNG_VERSION
295   RETVAL = newSVpv( STRINGIFY(PNG_VERSION), 0 );
296 #else
297   RETVAL = &PL_sv_undef;
298 #endif
299 }
300 OUTPUT:
301   RETVAL
302 
303 SV *
gif_version(void)304 gif_version(void)
305 CODE:
306 {
307 #ifdef GIF_VERSION
308   RETVAL = newSVpv( STRINGIFY(GIF_VERSION), 0 );
309 #else
310   RETVAL = &PL_sv_undef;
311 #endif
312 }
313 OUTPUT:
314   RETVAL
315 
316