1 
2 /**
3  * IMAGE OPCODES
4  *
5  * imageOpcodes.c
6  *
7  * Copyright (c) 2007 by Cesare Marilungo. All rights reserved.
8  *
9  * L I C E N S E
10  *
11  * This software is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This software is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this software; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
24  *
25  * C S O U N D   M A N   P A G E
26  *
27  * imageload            - opens an image (in PNG format)
28  * imagesave            - saves an image (in PNG format)
29  * imagecreate          - create an empty image
30  * imagesize            - gets the size of an image
31  * imagegetpixel        - gets the rgb values of a pixel
32  * imagesetpixel        - sets the rgb values of a pixel
33  * imagefree            - free memory from a previously loaded or created image.
34  *
35  * SYNTAX
36  *
37  * iImageNumber imageload sFilename
38  *
39  *              imageSave iImageNumber, sFilename
40  *
41  * iImageNumber imagecreate iWidth, iHeight
42  *
43  * iWidth, iHeight imagesize iImageNumber
44  *
45  * kred, kgreen, kblue imagegetpixel iImageNumber, kX, kY
46  * ared, agreen, ablue imagegetpixel iImageNumber, aX, aY
47  *
48  * imagesetpixel iImageNumber, kX, kY, kred, kgreen, kblue
49  * imagesetpixel iImageNumber, aX, aY, ared, agreen, ablue
50  *
51  *              imageFree iImageNumber
52  *
53  */
54 
55 #define USE_LIBPNG
56 
57 #ifdef USE_LIBPNG
58 #include <png.h>
59 #else
60 #include <Imlib.h>
61 #include <X11/Xlib.h>
62 #endif
63 
64 /* #include <SDL/SDL.h> */
65 /* #include <SDL/SDL_image.h> */
66 
67 #include "csdl.h"
68 #include "imageOpcodes.h"
69 
70 /*
71   __doOpenImage and __doSaveImage are the only two functions that deal
72   with the actual image loading saving library.
73 
74   imageData stores the image content as an array of RGB bytes for each
75   row (l->r t->b) so a pixel value in imageData is located at: (w*y+x)*3.
76 */
77 
78 /* #undef CS_KSMPS */
79 /* #define CS_KSMPS     (csound->GetKsmps(csound)) */
80 
__doOpenImage(char * filename,CSOUND * csound)81 static Image * __doOpenImage(char * filename, CSOUND *csound)
82 {
83 #ifdef USE_LIBPNG
84 #define HS (8)
85     FILE *fp;
86     void *fd;
87     unsigned char header[HS];
88     png_structp png_ptr;
89     png_infop info_ptr;
90     /* png_infop end_ptr; */
91     int32_t is_png;
92     png_uint_32 width, height, rowbytes;
93     int32_t bit_depth;
94     int32_t color_type;
95     unsigned char *image_data;
96     png_bytepp row_pointers;
97     uint32_t i;
98 
99     Image *img;
100 
101     fd = csound->FileOpen2(csound, &fp, CSFILE_STD, filename, "rb",
102                            "SFDIR;SSDIR", CSFTYPE_IMAGE_PNG, 0);
103     if (UNLIKELY(fd == NULL)) {
104       csound->InitError(csound,
105                         Str("imageload: cannot open image %s.\n"), filename);
106       return NULL;
107     }
108 
109     if (UNLIKELY(HS!=fread(header, 1, HS, fp)))
110       csound->InitError(csound,
111                         Str("imageload: file %s is not in PNG format.\n"),
112                         filename);
113     is_png = !png_sig_cmp(header, 0, HS);
114 
115     if (UNLIKELY(!is_png)) {
116       csound->InitError(csound,
117                         Str("imageload: file %s is not in PNG format.\n"),
118                         filename);
119       csound->FileClose(csound, fd);
120       return NULL;
121     }
122 
123     png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
124     if (UNLIKELY(!png_ptr)) {
125       csound->InitError(csound, "%s", Str("imageload: out of memory.\n"));
126       csound->FileClose(csound, fd);
127       return NULL;
128     }
129     info_ptr = png_create_info_struct(png_ptr);
130     if (UNLIKELY(!info_ptr)) {
131       png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL);
132       csound->InitError(csound, "%s", Str("imageload: out of memory.\n"));
133       csound->FileClose(csound, fd);
134       return NULL;
135     }
136 
137     /* end_ptr = png_create_info_struct(png_ptr); */
138     /* if (UNLIKELY(!end_ptr)) { */
139     /*   png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); */
140     /*   csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); */
141     /*   csound->FileClose(csound, fd); */
142     /*   return NULL; */
143     /* } */
144 
145     png_init_io(png_ptr, fp);
146     png_set_sig_bytes(png_ptr, HS);
147 
148     png_read_info(png_ptr, info_ptr);
149     {
150 
151       png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
152                    &color_type, NULL, NULL, NULL);
153     }
154     if (color_type & PNG_COLOR_MASK_ALPHA)
155       png_set_strip_alpha(png_ptr);
156     if (bit_depth == 16)
157       png_set_strip_16(png_ptr);
158     if (bit_depth < 8)
159       png_set_packing(png_ptr);
160     if (color_type == PNG_COLOR_TYPE_GRAY ||
161         color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
162       png_set_gray_to_rgb(png_ptr);
163     if (color_type == PNG_COLOR_TYPE_PALETTE)
164       png_set_palette_to_rgb(png_ptr);
165 
166     png_read_update_info(png_ptr, info_ptr);
167     rowbytes = png_get_rowbytes(png_ptr, info_ptr);
168 
169     if (UNLIKELY((image_data =
170                  (unsigned char *)csound->Malloc(csound,rowbytes*height))==NULL)) {
171       png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
172       csound->InitError(csound, "%s", Str("imageload: out of memory.\n"));
173       return NULL;
174     }
175 
176     row_pointers = (png_bytepp) csound->Malloc(csound, height*sizeof(png_bytep));
177     if (UNLIKELY(row_pointers == NULL)) {
178       png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
179       csound->Free(csound,image_data);
180       image_data = NULL;
181       csound->InitError(csound, "%s", Str("imageload: out of memory.\n"));
182       return NULL;
183     }
184 
185     for (i = 0; i < height; i++)
186       row_pointers[i] = image_data + i*rowbytes;
187 
188     png_read_image(png_ptr, row_pointers);
189     csound->Free(csound,row_pointers);
190     png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
191     csound->FileClose(csound, fd);
192 
193     img = csound->Malloc(csound, sizeof(Image));
194     if (UNLIKELY(!img)) {
195       csound->Free(csound,image_data);
196       csound->InitError(csound, "%s", Str("imageload: out of memory.\n"));
197       return NULL;
198     }
199 
200     img->w = width;
201     img->h = height;
202     img->imageData = image_data;
203 
204     return img;
205 
206 #else
207 
208     Display *disp;
209     ImlibData *id;
210     ImlibImage *im;
211     Image *img;
212     size_t datasize;
213 
214     disp=XOpenDisplay(NULL);
215     id=Imlib_init(disp);
216     im=Imlib_load_image(id, filename);
217 
218     img = csound->Malloc(csound, sizeof(Image));
219     img->w = im->rgb_width;
220     img->h = im->rgb_height;
221     datasize = img->w*img->h*3 * sizeof(unsigned char);
222     img->imageData = csound->Malloc(csound, datasize);
223     memcpy(img->imageData, im->rgb_data, datasize);
224 
225     return img;
226 #endif
227 
228     /* SDL */
229 /*  Image *img;
230     size_t datasize;
231     SDL_Surface *srfc;
232     int32_t x,y;
233     int32_t bpp;
234     int32_t indcount = 0;
235     Uint32 *pixel;
236     Uint8 r, g, b;
237 
238     srfc = IMG_Load(filename);
239     if (srfc) {
240         SDL_LockSurface(srfc);
241         img = csound->Malloc(csound, sizeof(Image));
242         img->w = srfc->w;
243         img->h = srfc->h;
244         bpp = srfc->format->BitsPerPixel;
245 
246         datasize = img->w*img->h*3 * sizeof(unsigned char);
247         img->imageData = csound->Malloc(csound, datasize);
248 
249         for(y = 0; y < img->h; y++) {
250             for(x = 0; x < img->w; x++) {
251                 if (bpp<=8) //need to test on other platforms
252                     pixel = srfc->pixels + y * srfc->pitch + x * bpp;
253                 else
254                     pixel = srfc->pixels + y * srfc->pitch + x * bpp / 8;
255                 SDL_GetRGB(*pixel,srfc->format, &r, &g, &b);
256                 img->imageData[indcount]= r;
257                 img->imageData[indcount+1]= g;
258                 img->imageData[indcount+2]= b;
259                 indcount += 3;
260             }
261         }
262         SDL_UnlockSurface(srfc);
263         SDL_FreeSurface ( srfc );
264         return img;
265   }
266 
267   return NULL;
268 */
269 
270 }
271 
272 
__doSaveImage(Image * image,char * filename,CSOUND * csound)273 static int32_t __doSaveImage(Image *image, char *filename, CSOUND *csound)
274 {
275 #ifdef USE_LIBPNG
276 
277     png_structp png_ptr;
278     png_infop info_ptr;
279     png_bytepp row_pointers;
280     uint32_t rowbytes;
281     int32_t i;
282 
283     FILE *fp;
284     void *fd;
285 
286     fd = csound->FileOpen2(csound, &fp, CSFILE_STD, filename, "wb",
287                            "", CSFTYPE_IMAGE_PNG, 0);
288     if (UNLIKELY(fd == NULL)) {
289       return
290         csound->InitError(csound,
291                           Str("imageload: cannot open image %s for writing.\n"),
292                           filename);
293     }
294 
295     png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
296 
297     if (UNLIKELY(!png_ptr)){
298       csound->FileClose(csound, fd);
299       return csound->InitError(csound, "%s", Str("imageload: out of memory.\n"));
300     }
301 
302     info_ptr = png_create_info_struct(png_ptr);
303     if (UNLIKELY(!info_ptr)) {
304       png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
305       csound->FileClose(csound, fd);
306       return csound->InitError(csound, "%s", Str("imageload: out of memory.\n"));
307     }
308 
309     png_init_io(png_ptr, fp);
310     png_set_IHDR(png_ptr, info_ptr, image->w, image->h,
311                  8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
312                  PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
313 
314     png_write_info(png_ptr, info_ptr);
315 
316     row_pointers = (png_bytepp)csound->Malloc(csound, image->h*sizeof(png_bytep));
317     if (UNLIKELY(row_pointers == NULL)) {
318       png_destroy_write_struct(&png_ptr, &info_ptr);
319       return csound->InitError(csound, "%s", Str("imageload: out of memory.\n"));
320     }
321 
322     rowbytes = png_get_rowbytes(png_ptr, info_ptr);
323 
324     for (i = 0; i < image->h; i++)
325       row_pointers[i] = image->imageData + i*rowbytes;
326 
327     png_write_image(png_ptr, row_pointers);
328     png_write_end(png_ptr, info_ptr);
329 
330     csound->Free(csound,row_pointers);
331     png_destroy_write_struct(&png_ptr, &info_ptr);
332     csound->FileClose(csound, fd);
333 
334     return OK;
335 #else
336     Display *disp;
337     ImlibData *id;
338     ImlibImage *im;
339 
340     disp=XOpenDisplay(NULL);
341     id=Imlib_init(disp);
342 
343     im = Imlib_create_image_from_data(id, image->imageData,
344                                       NULL, image->w, image->h);
345     Imlib_save_image(id, im, filename, NULL);
346     Imlib_kill_image(id, im);
347 
348     return OK;
349 #endif
350 }
351 
352 
createImage(CSOUND * csound,int32_t w,int32_t h)353 static Image * createImage(CSOUND *csound, int32_t w, int32_t h)
354 {
355     Image *img;
356     size_t datasize;
357 
358     img = csound->Malloc(csound, sizeof(Image));
359     img->w = w;
360     img->h = h;
361 
362     datasize = img->w*img->h*3 * sizeof(unsigned char);
363     img->imageData = csound->Malloc(csound, datasize);
364 
365     return img;
366 }
367 
368 
getImages(CSOUND * csound)369 static Images * getImages(CSOUND *csound)
370 {
371     Images *pimages;
372     pimages = (Images*)csound->QueryGlobalVariable(csound,
373                                                    "imageOpcodes.images");
374     if (pimages==NULL) { /* first call */
375       csound->CreateGlobalVariable(csound, "imageOpcodes.images",
376                                    sizeof(Images));
377       pimages = (Images*)csound->QueryGlobalVariable(csound,
378                                                      "imageOpcodes.images");
379       pimages->images = (Image **) NULL;
380       pimages->cnt = (size_t) 0;
381     }
382     return pimages;
383 }
384 
imagecreate(CSOUND * csound,IMGCREATE * p)385 static int32_t imagecreate (CSOUND *csound, IMGCREATE * p)
386 {
387     Images *pimages;
388     Image *img;
389 
390     pimages = getImages(csound);
391 
392     pimages->cnt++;
393     pimages->images =
394       (Image **) csound->ReAlloc(csound, pimages->images,
395                                  sizeof(Image *) * pimages->cnt);
396 
397     img = createImage(csound, *p->kw, *p->kh);
398 
399     if (UNLIKELY(img==NULL)) {
400       return csound->InitError(csound, "%s", Str("Cannot allocate memory.\n"));
401     }
402     else {
403       pimages->images[pimages->cnt-1] = img;
404       *(p->kn) = (MYFLT) pimages->cnt;
405       return OK;
406     }
407 }
408 
imageload(CSOUND * csound,IMGLOAD * p)409 static int32_t imageload (CSOUND *csound, IMGLOAD * p)
410 {
411     char filename[256];
412     Images *pimages;
413     Image *img;
414 
415     pimages = getImages(csound);
416 
417     pimages->cnt++;
418     pimages->images =
419       (Image **) csound->ReAlloc(csound, pimages->images,
420                                  sizeof(Image *) * pimages->cnt);
421 
422     strncpy(filename, (char*) (p->ifilnam->data), 255); filename[255]='\0';
423 
424     img = __doOpenImage(filename, csound);
425 
426     if (LIKELY(img)) {
427       pimages->images[pimages->cnt-1] = img;
428       *(p->kn) = (MYFLT) pimages->cnt;
429       return OK;
430     }
431     else {
432       return csound->InitError(csound,
433                                Str("imageload: cannot open image %s.\n"),
434                                filename);
435     }
436 }
437 
438 
imagesize(CSOUND * csound,IMGSIZE * p)439 static int32_t imagesize (CSOUND *csound, IMGSIZE * p)
440 {
441     Images *pimages;
442     Image *img;
443 
444     pimages = (Images *) csound->QueryGlobalVariable(csound,
445                                                      "imageOpcodes.images");
446     img = pimages->images[(int32_t)(*p->kn)-1];
447 
448     *(p->kw) = (MYFLT) img->w;
449     *(p->kh) = (MYFLT) img->h;
450     return OK;
451 }
452 
453 
imagegetpixel(CSOUND * csound,IMGGETPIXEL * p)454 static int32_t imagegetpixel (CSOUND *csound, IMGGETPIXEL * p)
455 {
456     Images *pimages;
457     Image *img;
458     int32_t w, h, x, y;
459 
460     pimages = (Images *) csound->QueryGlobalVariable(csound,
461                                                      "imageOpcodes.images");
462     img = pimages->images[(int32_t)(*p->kn)-1];
463 
464     w = img->w;
465     h = img->h;
466 
467     x = *p->kx*w;
468     y = *p->ky*h;
469 
470     if (x >= 0 && x < w && y >= 0 && y < h ) {
471       int32_t pixel = (w*y+x)*3;
472       *p->kr = img->imageData[pixel]/FL(255.0);
473       *p->kg = img->imageData[pixel+1]/FL(255.0);
474       *p->kb = img->imageData[pixel+2]/FL(255.0);
475     }
476     else {
477       *p->kr = FL(0.0);
478       *p->kg = FL(0.0);
479       *p->kb = FL(0.0);
480     }
481 
482     return OK;
483 }
484 
imagegetpixel_a(CSOUND * csound,IMGGETPIXEL * p)485 static int32_t imagegetpixel_a (CSOUND *csound, IMGGETPIXEL * p)
486 {
487     Images *pimages;
488     Image *img;
489 
490     MYFLT *r = p->kr;
491     MYFLT *g = p->kg;
492     MYFLT *b = p->kb;
493 
494     MYFLT *tx = p->kx;
495     MYFLT *ty = p->ky;
496 
497     uint32_t offset = p->h.insdshead->ksmps_offset;
498     uint32_t early  = p->h.insdshead->ksmps_no_end;
499     uint32_t i, nsmps = CS_KSMPS;
500     int32_t w, h, x, y, pixel;
501 
502     pimages = (Images *) csound->QueryGlobalVariable(csound,
503                                                      "imageOpcodes.images");
504     img = pimages->images[(int32_t)(*p->kn)-1];
505     w = img->w;
506     h = img->h;
507 
508 
509     if (UNLIKELY(offset)) {
510       memset(r, '\0', offset*sizeof(MYFLT));
511       memset(g, '\0', offset*sizeof(MYFLT));
512       memset(b, '\0', offset*sizeof(MYFLT));
513     }
514     if (UNLIKELY(early)) {
515       nsmps -= early;
516       memset(&r[nsmps], '\0', early*sizeof(MYFLT));
517       memset(&g[nsmps], '\0', early*sizeof(MYFLT));
518       memset(&b[nsmps], '\0', early*sizeof(MYFLT));
519     }
520     for (i = 0; i < nsmps; i++) {
521 
522       x = tx[i]*w;
523       y = ty[i]*h;
524 
525       if ( x >= 0 && x < w && y >= 0 && y < h ) {
526         pixel = (w*y+x)*3;
527         r[i] = img->imageData[pixel]/FL(255.0);
528         g[i] = img->imageData[pixel+1]/FL(255.0);
529         b[i] = img->imageData[pixel+2]/FL(255.0);
530       }
531       else {
532         r[i] = FL(0.0);
533         g[i] = FL(0.0);
534         b[i] = FL(0.0);
535       }
536     }
537 
538     return OK;
539 }
540 
imagesetpixel_a(CSOUND * csound,IMGSETPIXEL * p)541 static int32_t imagesetpixel_a (CSOUND *csound, IMGSETPIXEL * p)
542 {
543     Images *pimages;
544     Image *img;
545 
546     MYFLT *r = p->kr;
547     MYFLT *g = p->kg;
548     MYFLT *b = p->kb;
549 
550     MYFLT *tx = p->kx;
551     MYFLT *ty = p->ky;
552 
553     uint32_t offset = p->h.insdshead->ksmps_offset;
554     uint32_t early  = p->h.insdshead->ksmps_no_end;
555     uint32_t i, nsmps = CS_KSMPS;
556     int32_t h,w,x, y, pixel;
557 
558     pimages = (Images *) csound->QueryGlobalVariable(csound,
559                                                      "imageOpcodes.images");
560     img = pimages->images[(int32_t)(*p->kn)-1];
561 
562     w = img->w;
563     h = img->h;
564 
565     if (UNLIKELY(early)) nsmps -= early;
566     for (i = offset; i < nsmps; i++) {
567 
568       x = tx[i]*w;
569       y = ty[i]*h;
570 
571       if (x >= 0 && x < w && y >= 0 && y < h ) {
572         pixel = (w*y+x)*3;
573         img->imageData[pixel] = (unsigned char)(r[i]*255) % 256;
574         img->imageData[pixel+1] = (unsigned char)(g[i]*255) % 256;
575         img->imageData[pixel+2] = (unsigned char)(b[i]*255) % 256;
576       }
577 
578     }
579 
580     return OK;
581 }
582 
imagesetpixel(CSOUND * csound,IMGSETPIXEL * p)583 static int32_t imagesetpixel (CSOUND *csound, IMGSETPIXEL * p)
584 {
585     Images *pimages;
586     Image *img;
587     int32_t w, h, x, y, pixel;
588 
589     pimages = (Images *) csound->QueryGlobalVariable(csound,
590                                                      "imageOpcodes.images");
591     img = pimages->images[(int32_t)(*p->kn)-1];
592 
593     w = img->w;
594     h = img->h;
595 
596     x = *p->kx*w;
597     y = *p->ky*h;
598 
599     if (x >= 0 && x < w && y >= 0 && y < h ) {
600       pixel = (w*y+x)*3;
601       img->imageData[pixel] = (unsigned char)((*p->kr)*255) % 256;
602       img->imageData[pixel+1] = (unsigned char)((*p->kg)*255) % 256;
603       img->imageData[pixel+2] = (unsigned char)((*p->kb)*255) % 256;
604     }
605     return OK;
606 }
607 
imagesave(CSOUND * csound,IMGSAVE * p)608 static int32_t imagesave (CSOUND *csound, IMGSAVE * p)
609 {
610     Images *pimages;
611     Image *img;
612     char filename[256];
613 
614     strncpy(filename, (char*) (p->ifilnam->data), 254); filename[255]='\0';
615 
616     pimages = (Images *) csound->QueryGlobalVariable(csound,
617                                                      "imageOpcodes.images");
618     img = pimages->images[(int32_t)(*p->kn)-1];
619 
620     return __doSaveImage(img, filename, csound);
621 }
622 
imagefree(CSOUND * csound,IMGSAVE * p)623 static int32_t imagefree (CSOUND *csound, IMGSAVE * p)
624 {
625     Images *pimages;
626     Image *img;
627 
628     pimages = (Images *) csound->QueryGlobalVariable(csound,
629                                                      "imageOpcodes.images");
630     img = pimages->images[(int32_t
631                            )(*p->kn)-1];
632     csound->Free(csound,img->imageData);
633     csound->Free(csound,img);
634 
635     return OK;
636 }
637 
638 #define S(x)    sizeof(x)
639 
640 static OENTRY image_localops[] = {
641   { "imageload",  S(IMGLOAD),  0, 1, "i", "S",   (SUBR)imageload, NULL, NULL   },
642   { "imagecreate",S(IMGCREATE),0, 1, "i", "ii",  (SUBR)imagecreate, NULL, NULL },
643   { "imagesize",  S(IMGSIZE),  0, 1, "ii", "i",  (SUBR)imagesize, NULL, NULL   },
644   { "imagegetpixel",  S(IMGGETPIXEL),  0, 3, "kkk", "ixx",
645     (SUBR)imagegetpixel, (SUBR)imagegetpixel,                                  },
646   { "imagegetpixel",  S(IMGGETPIXEL),  0, 3, "aaa", "ixx",
647     (SUBR)imagegetpixel, (SUBR)imagegetpixel_a                           },
648   { "imagesetpixel",  S(IMGSETPIXEL),  0, 3, "", "ikkkkk",
649     (SUBR)imagesetpixel, (SUBR)imagesetpixel, (SUBR)imagesetpixel_a            },
650   { "imagesetpixel",  S(IMGSETPIXEL),  0, 3, "", "iaaaaa",
651     (SUBR)imagesetpixel, (SUBR)imagesetpixel_a                          },
652   { "imagesave",  S(IMGSAVE),  0, 1, "", "iS",   (SUBR)imagesave, NULL, NULL   },
653   { "imagefree",  S(IMGFREE),  0, 1, "", "i",    (SUBR)imagefree, NULL, NULL   },
654 };
655 
656 
657 LINKAGE_BUILTIN(image_localops)
658