+ else {
+ return !memcmp(magic->magic, buffer, magic->magic_size);
+ }
+}
+
+/*
+=item i_test_format_probe(io_glue *data, int length)
+
+Check the beginning of the supplied file for a 'magic number'
+
+=cut
+*/
+
+#define FORMAT_ENTRY(magic, type) \
+ { (unsigned char *)(magic ""), sizeof(magic)-1, type }
+#define FORMAT_ENTRY2(magic, type, mask) \
+ { (unsigned char *)(magic ""), sizeof(magic)-1, type, (unsigned char *)(mask) }
+
+const char *
+i_test_format_probe(io_glue *data, int length) {
+ static const struct magic_entry formats[] = {
+ FORMAT_ENTRY("\xFF\xD8", "jpeg"),
+ FORMAT_ENTRY("GIF87a", "gif"),
+ FORMAT_ENTRY("GIF89a", "gif"),
+ FORMAT_ENTRY("MM\0*", "tiff"),
+ FORMAT_ENTRY("II*\0", "tiff"),
+ FORMAT_ENTRY("BM", "bmp"),
+ FORMAT_ENTRY("\x89PNG\x0d\x0a\x1a\x0a", "png"),
+ FORMAT_ENTRY("P1", "pnm"),
+ FORMAT_ENTRY("P2", "pnm"),
+ FORMAT_ENTRY("P3", "pnm"),
+ FORMAT_ENTRY("P4", "pnm"),
+ FORMAT_ENTRY("P5", "pnm"),
+ FORMAT_ENTRY("P6", "pnm"),
+ FORMAT_ENTRY("/* XPM", "xpm"),
+ FORMAT_ENTRY("\x8aMNG", "mng"),
+ FORMAT_ENTRY("\x8aJNG", "jng"),
+ /* SGI RGB - with various possible parameters to avoid false positives
+ on similar files
+ values are: 2 byte magic, rle flags (0 or 1), bytes/sample (1 or 2)
+ */
+ FORMAT_ENTRY("\x01\xDA\x00\x01", "sgi"),
+ FORMAT_ENTRY("\x01\xDA\x00\x02", "sgi"),
+ FORMAT_ENTRY("\x01\xDA\x01\x01", "sgi"),
+ FORMAT_ENTRY("\x01\xDA\x01\x02", "sgi"),
+
+ FORMAT_ENTRY2("FORM ILBM", "ilbm", "xxxx xxxx"),
+
+ /* different versions of PCX format
+ http://www.fileformat.info/format/pcx/
+ */
+ FORMAT_ENTRY("\x0A\x00\x01", "pcx"),
+ FORMAT_ENTRY("\x0A\x02\x01", "pcx"),
+ FORMAT_ENTRY("\x0A\x03\x01", "pcx"),
+ FORMAT_ENTRY("\x0A\x04\x01", "pcx"),
+ FORMAT_ENTRY("\x0A\x05\x01", "pcx"),
+
+ /* FITS - http://fits.gsfc.nasa.gov/ */
+ FORMAT_ENTRY("SIMPLE =", "fits"),
+
+ /* PSD - Photoshop */
+ FORMAT_ENTRY("8BPS\x00\x01", "psd"),
+
+ /* EPS - Encapsulated Postscript */
+ /* only reading 18 chars, so we don't include the F in EPSF */
+ FORMAT_ENTRY("%!PS-Adobe-2.0 EPS", "eps"),
+
+ /* Utah RLE */
+ FORMAT_ENTRY("\x52\xCC", "utah"),
+
+ /* GZIP compressed, only matching deflate for now */
+ FORMAT_ENTRY("\x1F\x8B\x08", "gzip"),
+
+ /* bzip2 compressed */
+ FORMAT_ENTRY("BZh", "bzip2"),
+
+ /* WEBP
+ http://code.google.com/speed/webp/docs/riff_container.html */
+ FORMAT_ENTRY2("RIFF WEBP", "webp", "xxxx xxxx"),
+
+ /* JPEG 2000
+ This might match a little loosely */
+ FORMAT_ENTRY("\x00\x00\x00\x0CjP \x0D\x0A\x87\x0A", "jp2"),
+ };
+ static const struct magic_entry more_formats[] = {
+ /* these were originally both listed as ico, but cur files can
+ include hotspot information */
+ FORMAT_ENTRY("\x00\x00\x01\x00", "ico"), /* Windows icon */
+ FORMAT_ENTRY("\x00\x00\x02\x00", "cur"), /* Windows cursor */
+ FORMAT_ENTRY2("\x00\x00\x00\x00\x00\x00\x00\x07",
+ "xwd", " xxxx"), /* X Windows Dump */
+ };
+
+ unsigned int i;
+ unsigned char head[18];
+ ssize_t rc;
+
+ rc = i_io_peekn(data, head, 18);
+ if (rc == -1) return NULL;
+#if 0
+ {
+ int i;
+ fprintf(stderr, "%d bytes -", (int)rc);
+ for (i = 0; i < rc; ++i)
+ fprintf(stderr, " %02x", head[i]);
+ fprintf(stderr, "\n");
+ }
+#endif
+
+ for(i=0; i<sizeof(formats)/sizeof(formats[0]); i++) {
+ struct magic_entry const *entry = formats + i;
+
+ if (test_magic(head, rc, entry))
+ return entry->name;
+ }
+
+ if ((rc == 18) &&
+ tga_header_verify(head))
+ return "tga";
+
+ for(i=0; i<sizeof(more_formats)/sizeof(more_formats[0]); i++) {
+ struct magic_entry const *entry = more_formats + i;
+
+ if (test_magic(head, rc, entry))
+ return entry->name;
+ }
+
+ return NULL;
+}
+
+/*
+=item i_img_is_monochrome(img, &zero_is_white)
+
+=category Image Information
+
+Tests an image to check it meets our monochrome tests.
+
+The idea is that a file writer can use this to test where it should
+write the image in whatever bi-level format it uses, eg. C<pbm> for
+C<pnm>.
+
+For performance of encoders we require monochrome images:
+
+=over
+
+=item *
+
+be paletted
+
+=item *
+
+have a palette of two colors, containing only C<(0,0,0)> and
+C<(255,255,255)> in either order.
+
+=back
+
+C<zero_is_white> is set to non-zero if the first palette entry is white.
+
+=cut
+*/
+
+int
+i_img_is_monochrome(i_img *im, int *zero_is_white) {
+ if (im->type == i_palette_type
+ && i_colorcount(im) == 2) {
+ i_color colors[2];
+ i_getcolors(im, 0, colors, 2);
+ if (im->channels == 3) {
+ if (colors[0].rgb.r == 255 &&
+ colors[0].rgb.g == 255 &&
+ colors[0].rgb.b == 255 &&
+ colors[1].rgb.r == 0 &&
+ colors[1].rgb.g == 0 &&
+ colors[1].rgb.b == 0) {
+ *zero_is_white = 1;
+ return 1;
+ }
+ else if (colors[0].rgb.r == 0 &&
+ colors[0].rgb.g == 0 &&
+ colors[0].rgb.b == 0 &&
+ colors[1].rgb.r == 255 &&
+ colors[1].rgb.g == 255 &&
+ colors[1].rgb.b == 255) {
+ *zero_is_white = 0;
+ return 1;
+ }
+ }
+ else if (im->channels == 1) {
+ if (colors[0].channel[0] == 255 &&
+ colors[1].channel[0] == 0) {
+ *zero_is_white = 1;
+ return 1;
+ }
+ else if (colors[0].channel[0] == 0 &&
+ colors[1].channel[0] == 255) {
+ *zero_is_white = 0;
+ return 1;
+ }
+ }
+ }
+
+ *zero_is_white = 0;
+ return 0;