00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 #ifdef HAVE_CONFIG_H
00017 # include "config.h"
00018 #elif !defined(__cplusplus) && !defined(inline)
00019 
00020 # define inline
00021 #endif
00022 #include "gif.h"
00023 #include <stdarg.h>
00024 #include <string.h>
00025 #ifdef __cplusplus
00026 extern "C" {
00027 #endif
00028 
00029 #define WRITE_BUFFER_SIZE       255
00030 #define NODES_SIZE              GIF_MAX_CODE
00031 #define LINKS_SIZE              GIF_MAX_CODE
00032 
00033 
00034 
00035 
00036 
00037 
00038 
00039 
00040 
00041 
00042 
00043 
00044 
00045 
00046 
00047 
00048 
00049 
00050 
00051 
00052 #define TABLE_TYPE              0
00053 #define LINKS_TYPE              1
00054 #define MAX_LINKS_TYPE          5
00055 typedef struct Gif_Node {
00056   
00057   Gif_Code code;
00058   byte type;
00059   byte suffix;
00060   struct Gif_Node *sibling;
00061   union {
00062     struct Gif_Node *s;
00063     struct Gif_Node **m;
00064   } child;
00065   
00066 } Gif_Node;
00067 
00068 
00069 typedef struct Gif_Context {
00070   
00071   Gif_Node *nodes;
00072   int nodes_pos;
00073   Gif_Node **links;
00074   int links_pos;
00075   
00076 } Gif_Context;
00077 
00078 
00079 typedef struct Gif_Writer {
00080   
00081   FILE *f;
00082   byte *v;
00083   u_int32_t pos;
00084   u_int32_t cap;
00085   int flags;
00086   int global_size;
00087   int local_size;
00088   void (*byte_putter)(byte, struct Gif_Writer *);
00089   void (*block_putter)(byte *, u_int16_t, struct Gif_Writer *);
00090   
00091 } Gif_Writer;
00092 
00093 
00094 #define gifputbyte(b, grr)      ((*grr->byte_putter)(b, grr))
00095 #define gifputblock(b, l, grr)  ((*grr->block_putter)(b, l, grr))
00096 
00097 static inline void
00098 gifputunsigned(u_int16_t uns, Gif_Writer *grr)
00099 {
00100   gifputbyte(uns & 0xFF, grr);
00101   gifputbyte(uns >> 8, grr);
00102 }
00103 
00104 
00105 static void
00106 file_byte_putter(byte b, Gif_Writer *grr)
00107 {
00108   fputc(b, grr->f);
00109 }
00110 
00111 static void
00112 file_block_putter(byte *block, u_int16_t size, Gif_Writer *grr)
00113 {
00114   fwrite(block, size, 1, grr->f);
00115 }
00116 
00117 
00118 static void
00119 memory_byte_putter(byte b, Gif_Writer *grr)
00120 {
00121   if (grr->pos >= grr->cap) {
00122     grr->cap *= 2;
00123     Gif_ReArray(grr->v, byte, grr->cap);
00124   }
00125   if (grr->v) {
00126     grr->v[grr->pos] = b;
00127     grr->pos++;
00128   }
00129 }
00130 
00131 static void
00132 memory_block_putter(byte *data, u_int16_t len, Gif_Writer *grr)
00133 {
00134   if (grr->pos + len >= grr->cap) {
00135     grr->cap *= 2;
00136     Gif_ReArray(grr->v, byte, grr->cap);
00137   }
00138   if (grr->v) {
00139     memcpy(grr->v + grr->pos, data, len);
00140     grr->pos += len;
00141   }
00142 }
00143 
00144 
00145 static void
00146 change_node_to_table(Gif_Context *gfc, Gif_Node *work_node,
00147                      Gif_Node *next_node, Gif_Code clear_code)
00148 {
00149   
00150   Gif_Code c;
00151   Gif_Node **table = &gfc->links[gfc->links_pos];
00152   Gif_Node *n;
00153   gfc->links_pos += clear_code;
00154   
00155   for (c = 0; c < clear_code; c++)
00156     table[c] = 0;
00157   table[next_node->suffix] = next_node;
00158   for (n = work_node->child.s; n; n = n->sibling)
00159     table[n->suffix] = n;
00160   
00161   work_node->type = TABLE_TYPE;
00162   work_node->child.m = table;
00163 }
00164 
00165 
00166 static void
00167 write_compressed_data(byte **img, u_int16_t width, u_int16_t height,
00168                       byte min_code_bits, Gif_Context *gfc, Gif_Writer *grr)
00169 {
00170   byte buffer[WRITE_BUFFER_SIZE];
00171   byte *buf;
00172   
00173   u_int16_t xleft;
00174   byte *imageline;
00175   
00176   u_int32_t leftover;
00177   byte bits_left_over;
00178   
00179   Gif_Node *work_node;
00180   Gif_Node *next_node;
00181   Gif_Code next_code;
00182   Gif_Code output_code;
00183   Gif_Code clear_code;
00184   Gif_Code eoi_code;
00185 #define CUR_BUMP_CODE (1 << cur_code_bits)
00186   byte suffix;
00187   
00188   byte cur_code_bits;
00189   
00190   
00191   gifputbyte(min_code_bits, grr);
00192   clear_code = 1 << min_code_bits;
00193   eoi_code = clear_code + 1;
00194   
00195   cur_code_bits = min_code_bits + 1;
00196   
00197   GIF_DEBUG(("clear(%d) eoi(%d) bits(%d)",clear_code,eoi_code,cur_code_bits));
00198   
00199   work_node = 0;
00200   output_code = clear_code;
00201   
00202 
00203   
00204   bits_left_over = 0;
00205   leftover = 0;
00206   buf = buffer;
00207   xleft = width;
00208   imageline = img[0];
00209   
00210   while (1) {
00211     
00212     
00213 
00214 
00215     leftover |= output_code << bits_left_over;
00216     bits_left_over += cur_code_bits;
00217     while (bits_left_over >= 8) {
00218       *buf++ = leftover & 0xFF;
00219       leftover = (leftover >> 8) & 0x00FFFFFF;
00220       bits_left_over -= 8;
00221       if (buf == buffer + WRITE_BUFFER_SIZE) {
00222         gifputbyte(WRITE_BUFFER_SIZE, grr);
00223         gifputblock(buffer, WRITE_BUFFER_SIZE, grr);
00224         buf = buffer;
00225       }
00226     }
00227     
00228     if (output_code == clear_code) {
00229       
00230       Gif_Code c;
00231       
00232       cur_code_bits = min_code_bits + 1;
00233       next_code = eoi_code + 1;
00234       
00235       
00236       gfc->nodes_pos = clear_code;
00237       gfc->links_pos = 0;
00238       for (c = 0; c < clear_code; c++) {
00239         gfc->nodes[c].code = c;
00240         gfc->nodes[c].type = LINKS_TYPE;
00241         gfc->nodes[c].suffix = c;
00242         gfc->nodes[c].child.s = 0;
00243       }
00244       
00245     } else if (next_code > CUR_BUMP_CODE) {
00246       
00247       if (cur_code_bits == GIF_MAX_CODE_BITS) {
00248         output_code = clear_code;
00249         continue;
00250       } else
00251         cur_code_bits++;
00252       
00253     } else if (output_code == eoi_code)
00254       break;
00255     
00256     
00257     
00258 
00259     
00260     
00261 
00262     while (height != 0) {
00263       suffix = *imageline;
00264       if (suffix >= clear_code)
00265         
00266         suffix = 0;
00267       if (!work_node)
00268         next_node = &gfc->nodes[suffix];
00269       else if (work_node->type == TABLE_TYPE)
00270         next_node = work_node->child.m[suffix];
00271       else
00272         for (next_node = work_node->child.s; next_node;
00273              next_node = next_node->sibling)
00274           if (next_node->suffix == suffix)
00275             break;
00276       
00277       imageline++;
00278       xleft--;
00279       if (xleft == 0) {
00280         xleft = width;
00281         height--;
00282         img++;
00283         imageline = img[0];
00284       }
00285       
00286       if (!next_node) {
00287         
00288 
00289 
00290         next_node = &gfc->nodes[gfc->nodes_pos];
00291         gfc->nodes_pos++;
00292         next_node->code = next_code;
00293         next_code++;
00294         next_node->type = LINKS_TYPE;
00295         next_node->suffix = suffix;
00296         next_node->child.s = 0;
00297         
00298         
00299         if (work_node->type == TABLE_TYPE)
00300           work_node->child.m[suffix] = next_node;
00301         else if (work_node->type < MAX_LINKS_TYPE
00302                  || gfc->links_pos + clear_code > LINKS_SIZE) {
00303           next_node->sibling = work_node->child.s;
00304           work_node->child.s = next_node;
00305           work_node->type++;
00306         } else
00307           change_node_to_table(gfc, work_node, next_node, clear_code);
00308         
00309         
00310         output_code = work_node->code;
00311         work_node = &gfc->nodes[suffix];
00312         goto found_output_code;
00313       }
00314       
00315       work_node = next_node;
00316     }
00317     
00318     
00319     output_code = (work_node ? work_node->code : eoi_code);
00320     work_node = 0;
00321     
00322    found_output_code: ;
00323   }
00324   
00325   if (bits_left_over > 0)
00326     *buf++ = leftover;
00327   
00328   if (buf != buffer) {
00329     GIF_DEBUG(("imageblock(%d)", buf - buffer));
00330     gifputbyte(buf - buffer, grr);
00331     gifputblock(buffer, buf - buffer, grr);
00332   }
00333   
00334   gifputbyte(0, grr);
00335 }
00336 
00337 
00338 static int
00339 calculate_min_code_bits(Gif_Stream *gfs, Gif_Image *gfi, Gif_Writer *grr)
00340 {
00341   int colors_used = -1, min_code_bits, i;
00342 
00343   if (grr->flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE) {
00344     
00345     if (grr->local_size > 0)
00346       colors_used = grr->local_size;
00347     else if (grr->global_size > 0)
00348       colors_used = grr->global_size;
00349     
00350   } else if (gfi->compressed) {
00351     
00352     colors_used = 1 << gfi->compressed[0];
00353   
00354   } else if (gfi->img) {
00355     
00356     int x, y, width = gfi->width, height = gfi->height;
00357     colors_used = 0;
00358     for (y = 0; y < height && colors_used < 128; y++) {
00359       byte *data = gfi->img[y];
00360       for (x = width; x > 0; x--, data++)
00361         if (*data > colors_used)
00362           colors_used = *data;
00363     }
00364     colors_used++;
00365     
00366   } else {
00367     
00368     colors_used = 256;
00369   }
00370   
00371   min_code_bits = 2;            
00372   i = 4;
00373   while (i < colors_used) {
00374     min_code_bits++;
00375     i *= 2;
00376   }
00377 
00378   if ((grr->flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE)
00379       && gfi->compressed && gfi->compressed[0] != min_code_bits) {
00380     
00381     if (Gif_UncompressImage(gfi))      
00382       Gif_FullCompressImage(gfs, gfi, grr->flags);
00383   }
00384   
00385   return min_code_bits;
00386 }
00387 
00388 static int
00389 write_image_data(Gif_Image *gfi, byte min_code_bits,
00390                  Gif_Context *gfc, Gif_Writer *grr)
00391 {
00392   byte **img = gfi->img;
00393   u_int16_t width = gfi->width, height = gfi->height;
00394     
00395   if (gfi->interlace) {
00396     u_int16_t y;
00397     byte **nimg = Gif_NewArray(byte *, height + 1);
00398     if (!nimg) return 0;
00399     
00400     for (y = 0; y < height; y++)
00401       nimg[y] = img[Gif_InterlaceLine(y, height)];
00402     nimg[height] = 0;
00403     
00404     write_compressed_data(nimg, width, height, min_code_bits, gfc, grr);
00405     
00406     Gif_DeleteArray(nimg);
00407   } else
00408     write_compressed_data(img, width, height, min_code_bits, gfc, grr);
00409   
00410   return 1;
00411 }
00412 
00413 
00414 static int get_color_table_size(Gif_Stream *, Gif_Image *, Gif_Writer *);
00415 
00416 int
00417 Gif_FullCompressImage(Gif_Stream *gfs, Gif_Image *gfi, int flags)
00418 {
00419   int ok = 0;
00420   byte min_code_bits;
00421   Gif_Writer grr;
00422   Gif_Context gfc;
00423   
00424   if (gfi->compressed && gfi->free_compressed) {
00425     (*gfi->free_compressed)((void *)gfi->compressed);
00426     gfi->compressed = 0;
00427   }
00428   
00429   gfc.nodes = Gif_NewArray(Gif_Node, NODES_SIZE);
00430   gfc.links = Gif_NewArray(Gif_Node *, LINKS_SIZE);
00431   
00432   grr.v = Gif_NewArray(byte, 1024);
00433   grr.pos = 0;
00434   grr.cap = 1024;
00435   grr.byte_putter = memory_byte_putter;
00436   grr.block_putter = memory_block_putter;
00437   grr.flags = flags;
00438   grr.global_size = get_color_table_size(gfs, 0, &grr);
00439   grr.local_size = get_color_table_size(gfs, gfi, &grr);
00440   
00441   if (!gfc.nodes || !gfc.links || !grr.v)
00442     goto done;
00443 
00444   min_code_bits = calculate_min_code_bits(gfs, gfi, &grr);
00445   ok = write_image_data(gfi, min_code_bits, &gfc, &grr);
00446   
00447  done:
00448   if (!ok) {
00449     Gif_DeleteArray(grr.v);
00450     grr.v = 0;
00451   }
00452   gfi->compressed = grr.v;
00453   gfi->compressed_len = grr.pos;
00454   gfi->free_compressed = Gif_DeleteArrayFunc;
00455   Gif_DeleteArray(gfc.nodes);
00456   Gif_DeleteArray(gfc.links);
00457   return grr.v != 0;
00458 }
00459 
00460 
00461 static int
00462 get_color_table_size(Gif_Stream *gfs, Gif_Image *gfi, Gif_Writer *grr)
00463 {
00464   Gif_Colormap *gfcm = (gfi ? gfi->local : gfs->global);
00465   int ncol, totalcol, i;
00466 
00467   if (!gfcm || gfcm->ncol <= 0)
00468     return 0;
00469 
00470   
00471   ncol = gfcm->ncol;
00472 
00473   
00474 
00475   if (grr->flags & GIF_WRITE_CAREFUL_MIN_CODE_SIZE) {
00476     if (gfi && gfi->transparent >= ncol)
00477       ncol = gfi->transparent + 1;
00478     else if (!gfi)
00479       for (i = 0; i < gfs->nimages; i++)
00480         if (gfs->images[i]->transparent >= ncol)
00481           ncol = gfs->images[i]->transparent + 1;
00482   }
00483 
00484   
00485   
00486   if (ncol > 256)
00487     ncol = 256;
00488   for (totalcol = 2; totalcol < ncol; totalcol *= 2)
00489     ;
00490   
00491   return totalcol;
00492 }
00493 
00494 static void
00495 write_color_table(Gif_Colormap *gfcm, int totalcol, Gif_Writer *grr)
00496 {
00497   Gif_Color *c = gfcm->col;
00498   int i, ncol = gfcm->ncol;
00499   
00500   for (i = 0; i < ncol && i < totalcol; i++, c++) {
00501     gifputbyte(c->red, grr);
00502     gifputbyte(c->green, grr);
00503     gifputbyte(c->blue, grr);
00504   }
00505   
00506   
00507   for (; i < totalcol; i++) {
00508     gifputbyte(0, grr);
00509     gifputbyte(0, grr);
00510     gifputbyte(0, grr);
00511   }
00512 }
00513 
00514 
00515 static int
00516 write_image(Gif_Stream *gfs, Gif_Image *gfi, Gif_Context *gfc, Gif_Writer *grr)
00517 {
00518   byte min_code_bits, packed = 0;
00519   grr->local_size = get_color_table_size(gfs, gfi, grr);
00520   
00521   gifputbyte(',', grr);
00522   gifputunsigned(gfi->left, grr);
00523   gifputunsigned(gfi->top, grr);
00524   gifputunsigned(gfi->width, grr);
00525   gifputunsigned(gfi->height, grr);
00526   
00527   if (grr->local_size > 0) {
00528     int size = 2;
00529     packed |= 0x80;
00530     while (size < grr->local_size)
00531       size *= 2, packed++;
00532   }
00533   
00534   if (gfi->interlace) packed |= 0x40;
00535   gifputbyte(packed, grr);
00536   
00537   if (grr->local_size > 0)
00538     write_color_table(gfi->local, grr->local_size, grr);
00539 
00540   
00541 
00542   min_code_bits = calculate_min_code_bits(gfs, gfi, grr);
00543   
00544   
00545 
00546 
00547   if (gfi->compressed) {
00548     byte *compressed = gfi->compressed;
00549     u_int32_t compressed_len = gfi->compressed_len;
00550     while (compressed_len > 0) {
00551       u_int16_t amt = (compressed_len > 0x7000 ? 0x7000 : compressed_len);
00552       gifputblock(compressed, amt, grr);
00553       compressed += amt;
00554       compressed_len -= amt;
00555     }
00556     
00557   } else
00558     write_image_data(gfi, min_code_bits, gfc, grr);
00559   
00560   return 1;
00561 }
00562 
00563 
00564 static void
00565 write_logical_screen_descriptor(Gif_Stream *gfs, Gif_Writer *grr)
00566 {
00567   byte packed = 0x70;           
00568   grr->global_size = get_color_table_size(gfs, 0, grr);
00569 
00570   Gif_CalculateScreenSize(gfs, 0);
00571   gifputunsigned(gfs->screen_width, grr);
00572   gifputunsigned(gfs->screen_height, grr);
00573   
00574   if (grr->global_size > 0) {
00575     u_int16_t size = 2;
00576     packed |= 0x80;
00577     while (size < grr->global_size && size < 256)
00578       size *= 2, packed++;
00579   }
00580   
00581   gifputbyte(packed, grr);
00582   gifputbyte(gfs->background, grr);
00583   gifputbyte(0, grr);           
00584   
00585   if (grr->global_size > 0)
00586     write_color_table(gfs->global, grr->global_size, grr);
00587 }
00588 
00589 
00590 
00591 
00592 
00593 
00594 
00595 
00596 
00597 
00598 static void
00599 write_graphic_control_extension(Gif_Image *gfi, Gif_Writer *grr)
00600 {
00601   byte packed = 0;
00602   gifputbyte('!', grr);
00603   gifputbyte(0xF9, grr);
00604   gifputbyte(4, grr);
00605   if (gfi->transparent >= 0) packed |= 0x01;
00606   packed |= (gfi->disposal & 0x07) << 2;
00607   gifputbyte(packed, grr);
00608   gifputunsigned(gfi->delay, grr);
00609   gifputbyte((byte)gfi->transparent, grr);
00610   gifputbyte(0, grr);
00611 }
00612 
00613 
00614 static void
00615 blast_data(byte *data, int len, Gif_Writer *grr)
00616 {
00617   while (len > 0) {
00618     int s = len > 255 ? 255 : len;
00619     gifputbyte(s, grr);
00620     gifputblock(data, s, grr);
00621     data += s;
00622     len -= s;
00623   }
00624   gifputbyte(0, grr);
00625 }
00626 
00627 
00628 static void
00629 write_name_extension(char *id, Gif_Writer *grr)
00630 {
00631   gifputbyte('!', grr);
00632   gifputbyte(0xCE, grr);
00633   blast_data((byte *)id, strlen(id), grr);
00634 }
00635 
00636 
00637 static void
00638 write_comment_extensions(Gif_Comment *gfcom, Gif_Writer *grr)
00639 {
00640   int i;
00641   for (i = 0; i < gfcom->count; i++) {
00642     gifputbyte('!', grr);
00643     gifputbyte(0xFE, grr);
00644     blast_data((byte *)gfcom->str[i], gfcom->len[i], grr);
00645   }
00646 }
00647 
00648 
00649 static void
00650 write_netscape_loop_extension(u_int16_t value, Gif_Writer *grr)
00651 {
00652   gifputblock((byte *)"!\xFF\x0BNETSCAPE2.0\x03\x01", 16, grr);
00653   gifputunsigned(value, grr);
00654   gifputbyte(0, grr);
00655 }
00656 
00657 
00658 static void
00659 write_generic_extension(Gif_Extension *gfex, Gif_Writer *grr)
00660 {
00661   u_int32_t pos = 0;
00662   if (gfex->kind < 0) return;   
00663   
00664   gifputbyte('!', grr);
00665   gifputbyte(gfex->kind, grr);
00666   if (gfex->kind == 255) {      
00667     int len = gfex->application ? strlen(gfex->application) : 0;
00668     if (len) {
00669       gifputbyte(len, grr);
00670       gifputblock((byte *)gfex->application, len, grr);
00671     }
00672   }
00673   while (pos + 255 < gfex->length) {
00674     gifputbyte(255, grr);
00675     gifputblock(gfex->data + pos, 255, grr);
00676     pos += 255;
00677   }
00678   if (pos < gfex->length) {
00679     u_int32_t len = gfex->length - pos;
00680     gifputbyte(len, grr); 
00681     gifputblock(gfex->data + pos, len, grr);
00682   }
00683   gifputbyte(0, grr);
00684 }
00685 
00686 
00687 static int
00688 write_gif(Gif_Stream *gfs, Gif_Writer *grr)
00689 {
00690   int ok = 0;
00691   int i;
00692   Gif_Image *gfi;
00693   Gif_Extension *gfex = gfs->extensions;
00694   Gif_Context gfc;
00695   
00696   gfc.nodes = Gif_NewArray(Gif_Node, NODES_SIZE);
00697   gfc.links = Gif_NewArray(Gif_Node *, LINKS_SIZE);
00698   if (!gfc.nodes || !gfc.links)
00699     goto done;
00700   
00701   {
00702     byte isgif89a = 0;
00703     if (gfs->comment || gfs->loopcount > -1)
00704       isgif89a = 1;
00705     for (i = 0; i < gfs->nimages && !isgif89a; i++) {
00706       gfi = gfs->images[i];
00707       if (gfi->identifier || gfi->transparent != -1 || gfi->disposal ||
00708           gfi->delay || gfi->comment)
00709         isgif89a = 1;
00710     }
00711     if (isgif89a)
00712       gifputblock((byte *)"GIF89a", 6, grr);
00713     else
00714       gifputblock((byte *)"GIF87a", 6, grr);
00715   }
00716   
00717   write_logical_screen_descriptor(gfs, grr);
00718   
00719   if (gfs->loopcount > -1)
00720     write_netscape_loop_extension(gfs->loopcount, grr);
00721   
00722   for (i = 0; i < gfs->nimages; i++) {
00723     Gif_Image *gfi = gfs->images[i];
00724     while (gfex && gfex->position == i) {
00725       write_generic_extension(gfex, grr);
00726       gfex = gfex->next;
00727     }
00728     if (gfi->comment)
00729       write_comment_extensions(gfi->comment, grr);
00730     if (gfi->identifier)
00731       write_name_extension(gfi->identifier, grr);
00732     if (gfi->transparent != -1 || gfi->disposal || gfi->delay)
00733       write_graphic_control_extension(gfi, grr);
00734     if (!write_image(gfs, gfi, &gfc, grr))
00735       goto done;
00736   }
00737   
00738   while (gfex) {
00739     write_generic_extension(gfex, grr);
00740     gfex = gfex->next;
00741   }
00742   if (gfs->comment)
00743     write_comment_extensions(gfs->comment, grr);
00744   
00745   gifputbyte(';', grr);
00746   ok = 1;
00747   
00748  done:
00749   Gif_DeleteArray(gfc.nodes);
00750   Gif_DeleteArray(gfc.links);
00751   return ok;
00752 }
00753 
00754 
00755 int
00756 Gif_FullWriteFile(Gif_Stream *gfs, int flags, FILE *f)
00757 {
00758   Gif_Writer grr;
00759   grr.f = f;
00760   grr.byte_putter = file_byte_putter;
00761   grr.block_putter = file_block_putter;
00762   grr.flags = flags;
00763   return write_gif(gfs, &grr);
00764 }
00765 
00766 
00767 #undef Gif_CompressImage
00768 #undef Gif_WriteFile
00769 
00770 int
00771 Gif_CompressImage(Gif_Stream *gfs, Gif_Image *gfi)
00772 {
00773   return Gif_FullCompressImage(gfs, gfi, 0);
00774 }
00775 
00776 int
00777 Gif_WriteFile(Gif_Stream *gfs, FILE *f)
00778 {
00779   return Gif_FullWriteFile(gfs, 0, f);
00780 }
00781 
00782 
00783 #ifdef __cplusplus
00784 }
00785 #endif