Back to index

texmacs  1.0.7.15
bmpimage.c
Go to the documentation of this file.
00001 /*  $Header: /home/cvsroot/dvipdfmx/src/bmpimage.c,v 1.2 2004/07/27 12:08:46 hirata Exp $
00002 
00003     This is dvipdfmx, an eXtended version of dvipdfm by Mark A. Wicks.
00004 
00005     Copyright (C) 2002 by Jin-Hwan Cho and Shunsaku Hirata,
00006     the dvipdfmx project team <dvipdfmx@project.ktug.or.kr>
00007     
00008     Copyright (C) 1998, 1999 by Mark A. Wicks <mwicks@kettering.edu>
00009 
00010     This program is free software; you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation; either version 2 of the License, or
00013     (at your option) any later version.
00014     
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019     
00020     You should have received a copy of the GNU General Public License
00021     along with this program; if not, write to the Free Software
00022     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
00023 */
00024 
00025 #if HAVE_CONFIG_H
00026 #include "config.h"
00027 #endif
00028 
00029 /*
00030  * BMP SUPPORT:
00031  */
00032 
00033 #include "system.h"
00034 #include "error.h"
00035 #include "mem.h"
00036 
00037 #include "pdfobj.h"
00038 
00039 #include "bmpimage.h"
00040 
00041 #define DIB_FILE_HEADER_SIZE 14
00042 #define DIB_CORE_HEADER_SIZE 14
00043 #define DIB_INFO_HEADER_SIZE 40
00044 
00045 #define DIB_COMPRESS_NONE 0
00046 #define DIB_COMPRESS_RLE8 1
00047 #define DIB_COMPRESS_RLE4 2
00048 
00049 #define DIB_HEADER_SIZE_MAX (DIB_FILE_HEADER_SIZE+DIB_INFO_HEADER_SIZE)
00050 
00051 static long read_raster_rle8 (unsigned char *data_ptr,
00052                            long width, long height, FILE *fp);
00053 static long read_raster_rle4 (unsigned char *data_ptr,
00054                            long width, long height, FILE *fp);
00055 
00056 int
00057 check_for_bmp (FILE *fp)
00058 {
00059  unsigned char sigbytes[2];
00060 
00061   if (!fp)
00062     return 0;
00063 
00064   rewind(fp);
00065   if (fread(sigbytes, 1, sizeof(sigbytes), fp) != sizeof(sigbytes) ||
00066       sigbytes[0] != 'B' || sigbytes[1] != 'M')
00067     return 0;
00068   else
00069     return 1;
00070   
00071   return 0;
00072 }
00073 
00074 int
00075 bmp_include_image (pdf_ximage *ximage, FILE *fp)
00076 {
00077   pdf_obj *stream, *stream_dict, *colorspace;
00078   ximage_info info;
00079   unsigned char  buf[DIB_HEADER_SIZE_MAX+4];
00080   unsigned char *p;
00081   long offset, fsize, hsize, compression;
00082   long psize; /* Bytes per palette color: 3 for OS2, 4 for Win */
00083   unsigned short bit_count; /* Bits per pix */
00084   int  num_palette, flip;
00085   int  i;
00086 
00087   pdf_ximage_init_image_info(&info);
00088 
00089   stream = stream_dict = colorspace = NULL;
00090   p = buf;
00091 
00092   rewind(fp);
00093   if (fread(buf, 1, DIB_FILE_HEADER_SIZE + 4, fp)
00094       != DIB_FILE_HEADER_SIZE + 4) {
00095     WARN("Could not read BMP file header...");
00096   }
00097 
00098   if (p[0] != 'B' || p[1] != 'M') {
00099     WARN("File not starting with \'B\' \'M\'... Not a BMP file?");
00100     return -1;
00101   }
00102   p += 2;
00103 
00104 #define ULONG_LE(b)  ((b)[0] + ((b)[1] << 8) +\
00105                     ((b)[2] << 16) + ((b)[3] << 24))
00106 #define USHORT_LE(b) ((b)[0] + ((b)[1] << 8))
00107 
00108   fsize  = ULONG_LE(p); p += 4;
00109   if (ULONG_LE(p) != 0) {
00110     WARN("Not a BMP file???");
00111     return -1;
00112   }
00113   p += 4;
00114   offset = ULONG_LE(p); p += 4;
00115 
00116   /* info header */
00117   hsize  = ULONG_LE(p); p += 4;
00118   if (fread(p, sizeof(char), hsize - 4, fp) != hsize - 4) {
00119     WARN("Could not read BMP file header...");
00120     return -1;
00121   }
00122   flip = 1;
00123   if (hsize == DIB_CORE_HEADER_SIZE) {
00124     info.width  = USHORT_LE(p); p += 2;
00125     info.height = USHORT_LE(p); p += 2;
00126     if (USHORT_LE(p) != 1) {
00127       WARN("Unknown bcPlanes value in BMP COREHEADER.");
00128       return -1;
00129     }
00130     p += 2;
00131     bit_count   = USHORT_LE(p); p += 2;
00132     compression = DIB_COMPRESS_NONE;
00133     psize = 3;
00134   } else if (hsize == DIB_INFO_HEADER_SIZE) {
00135     info.width  = ULONG_LE(p);  p += 4;
00136     info.height = ULONG_LE(p);  p += 4;
00137     if (USHORT_LE(p) != 1) {
00138       WARN("Unknown biPlanes value in BMP INFOHEADER.");
00139       return -1;
00140     }
00141     p += 2;
00142     bit_count   = USHORT_LE(p); p += 2;
00143     compression = ULONG_LE(p);  p += 4;
00144     if (info.height < 0) {
00145       info.height = -info.height;
00146       flip = 0;
00147     }
00148     psize = 4;
00149   } else {
00150     WARN("Unknown BMP header type.");
00151     return -1;
00152   }
00153 
00154   if (bit_count < 24) {
00155     if (bit_count != 1 &&
00156        bit_count != 4 && bit_count != 8) {
00157       WARN("Unsupported palette size: %ld", bit_count);
00158       return -1;
00159     }
00160     num_palette = (offset - hsize - DIB_FILE_HEADER_SIZE) / psize;
00161     info.bits_per_component = bit_count;
00162     info.num_components = 1;
00163   } else if (bit_count == 24) { /* full color */
00164     num_palette = 1; /* dummy */
00165     info.bits_per_component = 8;
00166     info.num_components = 3;
00167   } else {
00168     WARN("Unkown BMP bitCount: %ld", bit_count);
00169     return -1;
00170   }
00171 
00172   if (info.width == 0 || info.height == 0 || num_palette < 1) {
00173     WARN("Invalid BMP file: width=%ld, height=%ld, #palette=%d",
00174         info.width, info.height, num_palette);
00175     return -1;
00176   }
00177 
00178   stream      = pdf_new_stream(STREAM_COMPRESS);
00179   stream_dict = pdf_stream_dict(stream);
00180 
00181   if (bit_count < 24) {
00182     pdf_obj *lookup;
00183     unsigned char *palette, bgrq[4];
00184 
00185     palette = NEW(num_palette*3+1, unsigned char);
00186     for (i = 0; i < num_palette; i++) {
00187       if (fread(bgrq, 1,  psize, fp) != psize) {
00188        WARN("Reading file failed...");
00189        RELEASE(palette);
00190        return -1;
00191       }
00192       /* BGR data */
00193       palette[3*i  ] = bgrq[2];
00194       palette[3*i+1] = bgrq[1];
00195       palette[3*i+2] = bgrq[0];
00196     }
00197     lookup = pdf_new_string(palette, num_palette*3);
00198     RELEASE(palette);
00199 
00200     colorspace = pdf_new_array();
00201     pdf_add_array(colorspace, pdf_new_name("Indexed"));
00202     pdf_add_array(colorspace, pdf_new_name("DeviceRGB"));
00203     pdf_add_array(colorspace, pdf_new_number(num_palette-1));
00204     pdf_add_array(colorspace, lookup);
00205   } else {
00206     colorspace = pdf_new_name("DeviceRGB");
00207   }
00208   pdf_add_dict(stream_dict, pdf_new_name("ColorSpace"), colorspace);
00209 
00210   /* Raster data of BMP is four-byte aligned. */
00211   {
00212     long rowbytes, n;
00213     unsigned char *stream_data_ptr = NULL;
00214 
00215     rowbytes = (info.width * bit_count + 7) / 8;
00216 
00217     seek_absolute(fp, offset);
00218     if (compression == DIB_COMPRESS_NONE) {
00219       long dib_rowbytes;
00220       int  padding;
00221 
00222       padding = (rowbytes % 4) ? 4 - (rowbytes % 4) : 0;
00223       dib_rowbytes = rowbytes + padding;
00224       stream_data_ptr = NEW(rowbytes*info.height + padding,
00225                          unsigned char);
00226       for (n = 0; n < info.height; n++) {
00227        p = stream_data_ptr + n * rowbytes;
00228        if (fread(p, 1, dib_rowbytes, fp) != dib_rowbytes) {
00229          WARN("Reading BMP raster data failed...");
00230          pdf_release_obj(stream);
00231          RELEASE(stream_data_ptr);
00232          return -1;
00233        }
00234       }
00235     } else if (compression == DIB_COMPRESS_RLE8) {
00236       stream_data_ptr = NEW(rowbytes*info.height, unsigned char);
00237       if (read_raster_rle8(stream_data_ptr,
00238                         info.width, info.height, fp) < 0) {
00239        WARN("Reading BMP raster data failed...");
00240        pdf_release_obj(stream);
00241        RELEASE(stream_data_ptr);
00242        return -1;
00243       }
00244     } else if (compression == DIB_COMPRESS_RLE4) {
00245       stream_data_ptr = NEW(rowbytes*info.height, unsigned char);
00246       if (read_raster_rle4(stream_data_ptr,
00247                         info.width, info.height, fp) < 0) {
00248        WARN("Reading BMP raster data failed...");
00249        pdf_release_obj(stream);
00250        RELEASE(stream_data_ptr);
00251        return -1;
00252       }
00253     } else {
00254       pdf_release_obj(stream);
00255       return -1;
00256     }
00257 
00258     /* gbr --> rgb */
00259     if (bit_count == 24) {
00260       for (n = 0; n < info.width * info.height * 3; n += 3) {
00261        unsigned char g;
00262        g = stream_data_ptr[n];
00263        stream_data_ptr[n  ] = stream_data_ptr[n+2];
00264        stream_data_ptr[n+2] = g;
00265       }
00266     }
00267 
00268     if (flip) {
00269       for (n = info.height - 1; n >= 0; n--) {
00270        p = stream_data_ptr + n * rowbytes;
00271        pdf_add_stream(stream, p, rowbytes);
00272       }
00273     } else {
00274       pdf_add_stream(stream, stream_data_ptr, rowbytes*info.height);
00275     }
00276     RELEASE(stream_data_ptr);
00277   }
00278 
00279   pdf_ximage_set_image(ximage, &info, stream);
00280 
00281   return 0;
00282 }
00283 
00284 static long
00285 read_raster_rle8 (unsigned char *data_ptr,
00286                 long width, long height, FILE *fp)
00287 {
00288   long count = 0;
00289   unsigned char *p, b0, b1;
00290   long h, v, rowbytes;
00291   int  eol, eoi;
00292 
00293   p = data_ptr;
00294   rowbytes = width;
00295   memset(data_ptr, 0, rowbytes*height);
00296   for (v = 0, eoi = 0; v < height && !eoi; v++) {
00297     for (h = 0, eol = 0; h < width && !eol; ) {
00298 
00299       b0 = get_unsigned_byte(fp);
00300       b1 = get_unsigned_byte(fp);
00301       count += 2;
00302 
00303       p = data_ptr + v * rowbytes + h;
00304 
00305       if (b0 == 0x00) {
00306        switch (b1) {
00307        case 0x00: /* EOL */
00308          eol = 1;
00309          break;
00310        case 0x01: /* EOI */
00311          eoi = 1;
00312          break;
00313        case 0x02:
00314          h += get_unsigned_byte(fp);
00315          v += get_unsigned_byte(fp);
00316          count += 2;
00317          break;
00318        default:
00319          h += b1;
00320          if (h > width) {
00321            WARN("RLE decode failed...");
00322            return -1;
00323          }
00324          if (fread(p, 1, b1, fp) != b1)
00325            return -1;
00326          count += b1;
00327          if (b1 % 2) {
00328            get_unsigned_byte(fp);
00329            count++;
00330          }
00331          break;
00332        }
00333       } else {
00334        h += b0;
00335        if (h > width) {
00336          WARN("RLE decode failed...");
00337          return -1;
00338        }
00339        memset(p, b1, b0);
00340       }
00341     }
00342 
00343     /* Check for EOL and EOI marker */
00344     if (!eol && !eoi) {
00345       b0 = get_unsigned_byte(fp);
00346       b1 = get_unsigned_byte(fp);
00347       if (b0 != 0x00) {
00348        WARN("RLE decode failed...");
00349        return -1;
00350       } else if (b1 == 0x01) {
00351        eoi = 1;
00352       } else if (b1 != 0x00) {
00353        WARN("RLE decode failed...");
00354        return -1;
00355       }
00356     }
00357 
00358     /* next row ... */
00359   }
00360 
00361   return count;
00362 }
00363 
00364 static long
00365 read_raster_rle4 (unsigned char *data_ptr,
00366                 long width, long height, FILE *fp)
00367 {
00368   long count = 0;
00369   unsigned char *p, b0, b1, b;
00370   long h, v, rowbytes;
00371   int  eol, eoi, i, nbytes;
00372 
00373   p = data_ptr;
00374   rowbytes = (width + 1) / 2;
00375   memset(data_ptr, 0, rowbytes*height);
00376   for (v = 0, eoi = 0; v < height && !eoi; v++) {
00377     for (h = 0, eol = 0; h < width && !eol; ) {
00378 
00379       b0 = get_unsigned_byte(fp);
00380       b1 = get_unsigned_byte(fp);
00381       count += 2;
00382 
00383       p  = data_ptr + v * rowbytes + (h / 2);
00384       if (b0 == 0x00) {
00385        switch (b1) {
00386        case 0x00: /* EOL */
00387          eol = 1;
00388          break;
00389        case 0x01: /* EOI */
00390          eoi = 1;
00391          break;
00392        case 0x02:
00393          h += get_unsigned_byte(fp);
00394          v += get_unsigned_byte(fp);
00395          count += 2;
00396          break;
00397        default:
00398          if (h + b1 > width) {
00399            WARN("RLE decode failed...");
00400            return -1;
00401          }
00402          nbytes = (b1 + 1)/2;
00403          if (h % 2) { /* starting at hi-nib */
00404            for (i = 0; i < nbytes; i++) {
00405              b = get_unsigned_byte(fp);
00406              *p++ |= (b >> 4) & 0x0f;
00407              *p    = (b << 4) & 0xf0;
00408            }
00409          } else {
00410            if (fread(p, 1, nbytes, fp) != nbytes) {
00411              return -1;
00412            }
00413          }
00414          h     += b1;
00415          count += nbytes;
00416          if (nbytes % 2) {
00417            get_unsigned_byte(fp);
00418            count++;
00419          }
00420          break;
00421        }
00422       } else {
00423        if (h + b0 > width) {
00424          WARN("RLE decode failed...");
00425          return -1;
00426        }
00427        if (h % 2) {
00428          *p++ = (b1 >> 4) & 0x0f;
00429          b1   = ((b1 << 4) & 0xf0)|((b1 >> 4) & 0x0f);
00430          b0--;
00431          h++;
00432        }
00433        nbytes = (b0 + 1)/2;
00434        memset(p, b1, nbytes);
00435        h += b0;
00436        if (h % 2)
00437          p[nbytes-1] &= 0xf0;
00438       }
00439     }
00440 
00441     /* Check for EOL and EOI marker */
00442     if (!eol && !eoi) {
00443       b0 = get_unsigned_byte(fp);
00444       b1 = get_unsigned_byte(fp);
00445       if (b0 != 0x00) {
00446        WARN("No EOL/EOI marker. RLE decode failed...");
00447        return -1;
00448       } else if (b1 == 0x01) {
00449        eoi = 1;
00450       } else if (b1 != 0x00) {
00451        WARN("No EOL/EOI marker. RLE decode failed...");
00452        return -1;
00453       }
00454     }
00455 
00456     /* next row ... */
00457   }
00458 
00459   return count;
00460 }