1 // ==========================================================
2 // PCX Loader
3 //
4 // Design and implementation by
5 // - Floris van den Berg (flvdberg@wxs.nl)
6 // - Jani Kajala (janik@remedy.fi)
7 // - Markus Loibl (markus.loibl@epost.de)
8 // - Herv� Drolon (drolon@infonie.fr)
9 // - Juergen Riecker (j.riecker@gmx.de)
10 //
11 // This file is part of FreeImage 3
12 //
13 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
14 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
15 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
16 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
17 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
18 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
19 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
20 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
21 // THIS DISCLAIMER.
22 //
23 // Use at your own risk!
24 // ==========================================================
25
26 #include "FreeImage.h"
27 #include "Utilities.h"
28
29 // ----------------------------------------------------------
30 // Constants + headers
31 // ----------------------------------------------------------
32
33 #define PCX_IO_BUF_SIZE 2048
34
35 // ----------------------------------------------------------
36
37 #ifdef _WIN32
38 #pragma pack(push, 1)
39 #else
40 #pragma pack(1)
41 #endif
42
43 /**
44 PCX header
45 */
46 typedef struct tagPCXHEADER {
47 BYTE manufacturer; // Magic number (0x0A = ZSoft Z)
48 BYTE version; // Version 0 == 2.5
49 // 2 == 2.8 with palette info
50 // 3 == 2.8 without palette info
51 // 5 == 3.0 with palette info
52 BYTE encoding; // Encoding: 0 = uncompressed, 1 = PCX bIsRLE compressed
53 BYTE bpp; // Bits per pixel per plane (only 1 or 8)
54 WORD window[4]; // left, upper, right,lower pixel coord.
55 WORD hdpi; // Horizontal resolution
56 WORD vdpi; // Vertical resolution
57 BYTE color_map[48]; // Colormap for 16-color images
58 BYTE reserved;
59 BYTE planes; // Number of planes (1, 3 or 4)
60 WORD bytes_per_line; // Bytes per row (always even)
61 WORD palette_info; // Palette information (1 = color or b&w; 2 = gray scale)
62 WORD h_screen_size;
63 WORD v_screen_size;
64 BYTE filler[54]; // Reserved filler
65 } PCXHEADER;
66
67 #ifdef _WIN32
68 #pragma pack(pop)
69 #else
70 #pragma pack()
71 #endif
72
73 // ==========================================================
74 // Internal functions
75 // ==========================================================
76
77 /**
78 Try to validate a PCX signature.
79 Note that a PCX file cannot be trusted by its signature.
80 We use other information from the PCX header to improve the trust we have with this file.
81 @return Returns TRUE if PCX signature is OK, returns FALSE otherwise
82 */
83 static BOOL
pcx_validate(FreeImageIO * io,fi_handle handle)84 pcx_validate(FreeImageIO *io, fi_handle handle) {
85 BYTE pcx_signature = 0x0A;
86 BYTE signature[4] = { 0, 0, 0, 0 };
87
88 if(io->read_proc(&signature, 1, 4, handle) != 4) {
89 return FALSE;
90 }
91 // magic number (0x0A = ZSoft Z)
92 if(signature[0] == pcx_signature) {
93 // version
94 if(signature[1] <= 5) {
95 // encoding
96 if((signature[2] == 0) || (signature[2] == 1)) {
97 // bits per pixel per plane
98 if((signature[3] == 1) || (signature[3] == 8)) {
99 return TRUE;
100 }
101 }
102 }
103 }
104
105 return FALSE;
106 }
107
108 /**
109 Read either run-length encoded or normal image data
110
111 THIS IS HOW RUNTIME LENGTH ENCODING WORKS IN PCX:
112 1) If the upper 2 bits of a byte are set, the lower 6 bits specify the count for the next byte
113 2) If the upper 2 bits of the byte are clear, the byte is actual data with a count of 1
114
115 Note that a scanline always has an even number of bytes
116
117 @param io FreeImage IO
118 @param handle FreeImage handle
119 @param buffer
120 @param length
121 @param bIsRLE
122 @param ReadBuf
123 @param ReadPos
124 @return
125 */
126 static unsigned
readLine(FreeImageIO * io,fi_handle handle,BYTE * buffer,unsigned length,BOOL bIsRLE,BYTE * ReadBuf,int * ReadPos)127 readLine(FreeImageIO *io, fi_handle handle, BYTE *buffer, unsigned length, BOOL bIsRLE, BYTE * ReadBuf, int * ReadPos) {
128 BYTE count = 0;
129 BYTE value = 0;
130 unsigned written = 0;
131
132 if (bIsRLE) {
133 // run-length encoded read
134
135 while (length--) {
136 if (count == 0) {
137 if (*ReadPos >= PCX_IO_BUF_SIZE - 1 ) {
138 if (*ReadPos == PCX_IO_BUF_SIZE - 1) {
139 // we still have one BYTE, copy it to the start pos
140 *ReadBuf = ReadBuf[PCX_IO_BUF_SIZE - 1];
141 io->read_proc(ReadBuf + 1, 1, PCX_IO_BUF_SIZE - 1, handle);
142 } else {
143 // read the complete buffer
144 io->read_proc(ReadBuf, 1, PCX_IO_BUF_SIZE, handle);
145 }
146
147 *ReadPos = 0;
148 }
149
150 value = *(ReadBuf + (*ReadPos)++);
151
152 if ((value & 0xC0) == 0xC0) {
153 count = value & 0x3F;
154 value = *(ReadBuf + (*ReadPos)++);
155 } else {
156 count = 1;
157 }
158 }
159
160 count--;
161
162 *(buffer + written++) = value;
163 }
164
165 } else {
166 // normal read
167
168 written = io->read_proc(buffer, length, 1, handle);
169 }
170
171 return written;
172 }
173
174 #ifdef FREEIMAGE_BIGENDIAN
175 static void
SwapHeader(PCXHEADER * header)176 SwapHeader(PCXHEADER *header) {
177 SwapShort(&header->window[0]);
178 SwapShort(&header->window[1]);
179 SwapShort(&header->window[2]);
180 SwapShort(&header->window[3]);
181 SwapShort(&header->hdpi);
182 SwapShort(&header->vdpi);
183 SwapShort(&header->bytes_per_line);
184 SwapShort(&header->palette_info);
185 SwapShort(&header->h_screen_size);
186 SwapShort(&header->v_screen_size);
187 }
188 #endif
189
190 // ==========================================================
191 // Plugin Interface
192 // ==========================================================
193
194 static int s_format_id;
195
196 // ==========================================================
197 // Plugin Implementation
198 // ==========================================================
199
200 /*!
201 Returns the format string for the plugin. Each plugin,
202 both internal in the DLL and external in a .fip file, must have
203 a unique format string to be addressable.
204 */
205
206 static const char * DLL_CALLCONV
Format()207 Format() {
208 return "PCX";
209 }
210
211 /*!
212 Returns a description string for the plugin. Though a
213 description is not necessary per-se,
214 it is advised to return an unique string in order to tell the
215 user what type of bitmaps this plugin will read and/or write.
216 */
217
218 static const char * DLL_CALLCONV
Description()219 Description() {
220 return "Zsoft Paintbrush";
221 }
222
223 /*!
224 Returns a comma separated list of file
225 extensions indicating what files this plugin can open. The
226 list, being used by FreeImage_GetFIFFromFilename, is usually
227 used as a last resort in finding the type of the bitmap we
228 are dealing with. Best is to check the first few bytes on
229 the low-level bits level first and compare them with a known
230 signature . If this fails, FreeImage_GetFIFFromFilename can be
231 used.
232 */
233
234 static const char * DLL_CALLCONV
Extension()235 Extension() {
236 return "pcx";
237 }
238
239 /*!
240 Returns an (optional) regular expression to help
241 software identifying a bitmap type. The
242 expression can be applied to the first few bytes (header) of
243 the bitmap. FreeImage is not capable of processing regular expression itself,
244 but FreeImageQt, the FreeImage Trolltech support library, can. If RegExpr
245 returns NULL, FreeImageQt will automatically bypass Trolltech's regular
246 expression support and use its internal functions to find the bitmap type.
247 */
248
249 static const char * DLL_CALLCONV
RegExpr()250 RegExpr() {
251 return NULL;
252 }
253
254 static const char * DLL_CALLCONV
MimeType()255 MimeType() {
256 return "image/x-pcx";
257 }
258
259 /*!
260 Validates a bitmap by reading the first few bytes
261 and comparing them with a known bitmap signature.
262 TRUE is returned if the bytes match the signature, FALSE otherwise.
263 The Validate function is used by using FreeImage_GetFileType.
264
265 Note: a plugin can safely read data any data from the bitmap without seeking back
266 to the original entry point; the entry point is stored prior to calling this
267 function and restored after.
268
269 Note: because of FreeImage's io redirection support, the header for the bitmap
270 must be on the start of the bitmap or at least on a known part in the bitmap. It is
271 forbidden to seek to the end of the bitmap or to a point relative to the end of a bitmap,
272 because the end of the bitmap is not always known.
273 */
274
275 static BOOL DLL_CALLCONV
Validate(FreeImageIO * io,fi_handle handle)276 Validate(FreeImageIO *io, fi_handle handle) {
277 return pcx_validate(io, handle);
278 }
279
280 /*!
281 This function is used to 'ask' the plugin if it can write
282 a bitmap in a certain bitdepth. Different bitmap types have different
283 capabilities, for example not all formats allow writing in palettized mode.
284 This function is there provide an uniform interface to the plugin's
285 capabilities. SupportsExportDepth returns TRUE if the plugin support writing
286 in the asked bitdepth, or FALSE if it doesn't. The function also
287 returns FALSE if bitmap saving is not supported by the plugin at all.
288 */
289
290 static BOOL DLL_CALLCONV
SupportsExportDepth(int depth)291 SupportsExportDepth(int depth) {
292 return FALSE;
293 }
294
295 static BOOL DLL_CALLCONV
SupportsExportType(FREE_IMAGE_TYPE type)296 SupportsExportType(FREE_IMAGE_TYPE type) {
297 return FALSE;
298 }
299
300 static BOOL DLL_CALLCONV
SupportsNoPixels()301 SupportsNoPixels() {
302 return TRUE;
303 }
304
305 // ----------------------------------------------------------
306
307 /*!
308 Loads a bitmap into memory. On entry it is assumed that
309 the bitmap to be loaded is of the correct type. If the bitmap
310 is of an incorrect type, the plugin might not gracefully fail but
311 crash or enter an endless loop. It is also assumed that all
312 the bitmap data is available at one time. If the bitmap is not complete,
313 for example because it is being downloaded while loaded, the plugin
314 might also not gracefully fail.
315
316 The Load function has the following parameters:
317
318 The first parameter (FreeImageIO *io) is a structure providing
319 function pointers in order to make use of FreeImage's IO redirection. Using
320 FreeImage's file i/o functions instead of standard ones it is garantueed
321 that all bitmap types, both current and future ones, can be loaded from
322 memory, file cabinets, the internet and more. The second parameter (fi_handle handle)
323 is a companion of FreeImageIO and can be best compared with the standard FILE* type,
324 in a generalized form.
325
326 The third parameter (int page) indicates wether we will be loading a certain page
327 in the bitmap or if we will load the default one. This parameter is only used if
328 the plugin supports multi-paged bitmaps, e.g. cabinet bitmaps that contain a series
329 of images or pages. If the plugin does support multi-paging, the page parameter
330 can contain either a number higher or equal to 0 to load a certain page, or -1 to
331 load the default page. If the plugin does not support multi-paging,
332 the page parameter is always -1.
333
334 The fourth parameter (int flags) manipulates the load function to load a bitmap
335 in a certain way. Every plugin has a different flag parameter with different meanings.
336
337 The last parameter (void *data) can contain a special data block used when
338 the file is read multi-paged. Because not every plugin supports multi-paging
339 not every plugin will use the data parameter and it will be set to NULL.However,
340 when the plugin does support multi-paging the parameter contains a pointer to a
341 block of data allocated by the Open function.
342 */
343
344 static FIBITMAP * DLL_CALLCONV
Load(FreeImageIO * io,fi_handle handle,int page,int flags,void * data)345 Load(FreeImageIO *io, fi_handle handle, int page, int flags, void *data) {
346 FIBITMAP *dib = NULL;
347 BYTE *bits; // Pointer to dib data
348 RGBQUAD *pal; // Pointer to dib palette
349 BYTE *line = NULL; // PCX raster line
350 BYTE *ReadBuf = NULL; // buffer;
351 BOOL bIsRLE; // True if the file is run-length encoded
352
353 if(!handle) {
354 return NULL;
355 }
356
357 BOOL header_only = (flags & FIF_LOAD_NOPIXELS) == FIF_LOAD_NOPIXELS;
358
359 try {
360 // check PCX identifier
361 // (note: should have been already validated using FreeImage_GetFileType but check again)
362 {
363 long start_pos = io->tell_proc(handle);
364 BOOL bValidated = pcx_validate(io, handle);
365 io->seek_proc(handle, start_pos, SEEK_SET);
366 if(!bValidated) {
367 throw FI_MSG_ERROR_MAGIC_NUMBER;
368 }
369 }
370
371 PCXHEADER header;
372
373 // process the header
374 if(io->read_proc(&header, sizeof(PCXHEADER), 1, handle) != 1) {
375 throw FI_MSG_ERROR_PARSING;
376 }
377 #ifdef FREEIMAGE_BIGENDIAN
378 SwapHeader(&header);
379 #endif
380
381 // process the window
382 const WORD *window = header.window; // left, upper, right,lower pixel coord.
383 const int left = window[0];
384 const int top = window[1];
385 const int right = window[2];
386 const int bottom = window[3];
387
388 // check image size
389 if((left >= right) || (top >= bottom)) {
390 throw FI_MSG_ERROR_PARSING;
391 }
392
393 const unsigned width = right - left + 1;
394 const unsigned height = bottom - top + 1;
395 const unsigned bitcount = header.bpp * header.planes;
396
397 // allocate a new dib
398 switch(bitcount) {
399 case 1:
400 case 4:
401 case 8:
402 dib = FreeImage_AllocateHeader(header_only, width, height, bitcount);
403 break;
404 case 24:
405 dib = FreeImage_AllocateHeader(header_only, width, height, bitcount, FI_RGBA_RED_MASK, FI_RGBA_GREEN_MASK, FI_RGBA_BLUE_MASK);
406 break;
407 default:
408 throw FI_MSG_ERROR_DIB_MEMORY;
409 break;
410 }
411
412 // if the dib couldn't be allocated, throw an error
413 if (!dib) {
414 throw FI_MSG_ERROR_DIB_MEMORY;
415 }
416
417 // metrics handling code
418
419 FreeImage_SetDotsPerMeterX(dib, (unsigned) (((float)header.hdpi) / 0.0254000 + 0.5));
420 FreeImage_SetDotsPerMeterY(dib, (unsigned) (((float)header.vdpi) / 0.0254000 + 0.5));
421
422 // Set up the palette if needed
423 // ----------------------------
424
425 switch(bitcount) {
426 case 1:
427 {
428 pal = FreeImage_GetPalette(dib);
429 pal[0].rgbRed = pal[0].rgbGreen = pal[0].rgbBlue = 0;
430 pal[1].rgbRed = pal[1].rgbGreen = pal[1].rgbBlue = 255;
431 break;
432 }
433
434 case 4:
435 {
436 pal = FreeImage_GetPalette(dib);
437
438 BYTE *pColormap = &header.color_map[0];
439
440 for (int i = 0; i < 16; i++) {
441 pal[i].rgbRed = pColormap[0];
442 pal[i].rgbGreen = pColormap[1];
443 pal[i].rgbBlue = pColormap[2];
444 pColormap += 3;
445 }
446
447 break;
448 }
449
450 case 8:
451 {
452 BYTE palette_id;
453
454 io->seek_proc(handle, -769L, SEEK_END);
455 io->read_proc(&palette_id, 1, 1, handle);
456
457 if (palette_id == 0x0C) {
458 BYTE *cmap = (BYTE*)malloc(768 * sizeof(BYTE));
459
460 if(cmap) {
461 io->read_proc(cmap, 768, 1, handle);
462
463 pal = FreeImage_GetPalette(dib);
464 BYTE *pColormap = &cmap[0];
465
466 for(int i = 0; i < 256; i++) {
467 pal[i].rgbRed = pColormap[0];
468 pal[i].rgbGreen = pColormap[1];
469 pal[i].rgbBlue = pColormap[2];
470 pColormap += 3;
471 }
472
473 free(cmap);
474 }
475
476 }
477
478 // wrong palette ID, perhaps a gray scale is needed ?
479
480 else if (header.palette_info == 2) {
481 pal = FreeImage_GetPalette(dib);
482
483 for(int i = 0; i < 256; i++) {
484 pal[i].rgbRed = (BYTE)i;
485 pal[i].rgbGreen = (BYTE)i;
486 pal[i].rgbBlue = (BYTE)i;
487 }
488 }
489
490 io->seek_proc(handle, (long)sizeof(PCXHEADER), SEEK_SET);
491 }
492 break;
493 }
494
495 if(header_only) {
496 // header only mode
497 return dib;
498 }
499
500 // calculate the line length for the PCX and the dib
501
502 // length of raster line in bytes
503 const unsigned lineLength = header.bytes_per_line * header.planes;
504 // length of dib line (rounded to DWORD) in bytes
505 const unsigned pitch = FreeImage_GetPitch(dib);
506
507 // run-length encoding ?
508
509 bIsRLE = (header.encoding == 1) ? TRUE : FALSE;
510
511 // load image data
512 // ---------------
513
514 line = (BYTE*)malloc(lineLength * sizeof(BYTE));
515 if(!line) {
516 throw FI_MSG_ERROR_MEMORY;
517 }
518
519 ReadBuf = (BYTE*)malloc(PCX_IO_BUF_SIZE * sizeof(BYTE));
520 if(!ReadBuf) {
521 throw FI_MSG_ERROR_MEMORY;
522 }
523
524 bits = FreeImage_GetScanLine(dib, height - 1);
525
526 int ReadPos = PCX_IO_BUF_SIZE;
527
528 if ((header.planes == 1) && ((header.bpp == 1) || (header.bpp == 8))) {
529 BYTE skip;
530 unsigned written;
531
532 for (unsigned y = 0; y < height; y++) {
533 // do a safe copy of the scanline into 'line'
534 written = readLine(io, handle, line, lineLength, bIsRLE, ReadBuf, &ReadPos);
535 // sometimes (already encountered), PCX images can have a lineLength > pitch
536 memcpy(bits, line, MIN(pitch, lineLength));
537
538 // skip trailing garbage at the end of the scanline
539
540 for (unsigned count = written; count < lineLength; count++) {
541 if (ReadPos < PCX_IO_BUF_SIZE) {
542 ReadPos++;
543 } else {
544 io->read_proc(&skip, sizeof(BYTE), 1, handle);
545 }
546 }
547
548 bits -= pitch;
549 }
550 } else if ((header.planes == 4) && (header.bpp == 1)) {
551 BYTE bit, mask, skip;
552 unsigned index;
553 BYTE *buffer;
554
555 buffer = (BYTE*)malloc(width * sizeof(BYTE));
556 if(!buffer) {
557 throw FI_MSG_ERROR_MEMORY;
558 }
559
560 for (unsigned y = 0; y < height; y++) {
561 unsigned written = readLine(io, handle, line, lineLength, bIsRLE, ReadBuf, &ReadPos);
562
563 // build a nibble using the 4 planes
564
565 memset(buffer, 0, width * sizeof(BYTE));
566
567 for(int plane = 0; plane < 4; plane++) {
568 bit = (BYTE)(1 << plane);
569
570 for (unsigned x = 0; x < width; x++) {
571 index = (unsigned)((x / 8) + plane * header.bytes_per_line);
572 mask = (BYTE)(0x80 >> (x & 0x07));
573 buffer[x] |= (line[index] & mask) ? bit : 0;
574 }
575 }
576
577 // then write the dib row
578
579 for (unsigned x = 0; x < width / 2; x++) {
580 bits[x] = (buffer[2*x] << 4) | buffer[2*x+1];
581 }
582
583 // skip trailing garbage at the end of the scanline
584
585 for (unsigned count = written; count < lineLength; count++) {
586 if (ReadPos < PCX_IO_BUF_SIZE) {
587 ReadPos++;
588 } else {
589 io->read_proc(&skip, sizeof(BYTE), 1, handle);
590 }
591 }
592
593 bits -= pitch;
594 }
595
596 free(buffer);
597
598 } else if((header.planes == 3) && (header.bpp == 8)) {
599 BYTE *pLine;
600
601 for (unsigned y = 0; y < height; y++) {
602 readLine(io, handle, line, lineLength, bIsRLE, ReadBuf, &ReadPos);
603
604 // convert the plane stream to BGR (RRRRGGGGBBBB -> BGRBGRBGRBGR)
605 // well, now with the FI_RGBA_x macros, on BIGENDIAN we convert to RGB
606
607 pLine = line;
608 unsigned x;
609
610 for (x = 0; x < width; x++) {
611 bits[x * 3 + FI_RGBA_RED] = pLine[x];
612 }
613 pLine += header.bytes_per_line;
614
615 for (x = 0; x < width; x++) {
616 bits[x * 3 + FI_RGBA_GREEN] = pLine[x];
617 }
618 pLine += header.bytes_per_line;
619
620 for (x = 0; x < width; x++) {
621 bits[x * 3 + FI_RGBA_BLUE] = pLine[x];
622 }
623 pLine += header.bytes_per_line;
624
625 bits -= pitch;
626 }
627 } else {
628 throw FI_MSG_ERROR_UNSUPPORTED_FORMAT;
629 }
630
631 free(line);
632 free(ReadBuf);
633
634 return dib;
635
636 } catch (const char *text) {
637 // free allocated memory
638
639 if (dib != NULL) {
640 FreeImage_Unload(dib);
641 }
642 if (line != NULL) {
643 free(line);
644 }
645 if (ReadBuf != NULL) {
646 free(ReadBuf);
647 }
648
649 FreeImage_OutputMessageProc(s_format_id, text);
650 }
651
652 return NULL;
653 }
654
655 // ==========================================================
656 // Init
657 // ==========================================================
658
659 /*!
660 Initialises the plugin. The first parameter (Plugin *plugin)
661 contains a pointer to a pre-allocated Plugin structure
662 wherein pointers to the available plugin functions
663 has to be stored. The second parameter (int format_id) is an identification
664 number that the plugin may use to show plugin specific warning messages
665 or other information to the user. The plugin number
666 is generated by FreeImage and can differ everytime the plugin is
667 initialised.
668
669 If you want to create your own plugin you have to take some
670 rules into account. Plugin functions have to be compiled
671 __stdcall using the multithreaded c runtime libraries. Throwing
672 exceptions in plugin functions is allowed, as long as those exceptions
673 are being caught inside the same plugin. It is forbidden for a plugin
674 function to directly call FreeImage functions or to allocate memory
675 and pass it to the main DLL. Exception to this rule is the special file data
676 block that may be allocated the Open function. Allocating a FIBITMAP inside a
677 plugin can be using the function allocate_proc in the FreeImage structure,
678 which will allocate the memory using the DLL's c runtime library.
679 */
680
681 void DLL_CALLCONV
InitPCX(Plugin * plugin,int format_id)682 InitPCX(Plugin *plugin, int format_id) {
683 s_format_id = format_id;
684
685 plugin->format_proc = Format;
686 plugin->description_proc = Description;
687 plugin->extension_proc = Extension;
688 plugin->regexpr_proc = RegExpr;
689 plugin->open_proc = NULL;
690 plugin->close_proc = NULL;
691 plugin->pagecount_proc = NULL;
692 plugin->pagecapability_proc = NULL;
693 plugin->load_proc = Load;
694 plugin->save_proc = NULL;
695 plugin->validate_proc = Validate;
696 plugin->mime_proc = MimeType;
697 plugin->supports_export_bpp_proc = SupportsExportDepth;
698 plugin->supports_export_type_proc = SupportsExportType;
699 plugin->supports_icc_profiles_proc = NULL;
700 plugin->supports_no_pixels_proc = SupportsNoPixels;
701 }
702