Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
6417 ashmew2 1
/*
2
 * wrbmp.c
3
 *
4
 * Copyright (C) 1994-1996, Thomas G. Lane.
5
 * This file is part of the Independent JPEG Group's software.
6
 * For conditions of distribution and use, see the accompanying README file.
7
 *
8
 * This file contains routines to write output images in Microsoft "BMP"
9
 * format (MS Windows 3.x and OS/2 1.x flavors).
10
 * Either 8-bit colormapped or 24-bit full-color format can be written.
11
 * No compression is supported.
12
 *
13
 * These routines may need modification for non-Unix environments or
14
 * specialized applications.  As they stand, they assume output to
15
 * an ordinary stdio stream.
16
 *
17
 * This code contributed by James Arthur Boucher.
18
 */
19
 
20
#include "cdjpeg.h"		/* Common decls for cjpeg/djpeg applications */
21
 
22
#ifdef BMP_SUPPORTED
23
 
24
 
25
/*
26
 * To support 12-bit JPEG data, we'd have to scale output down to 8 bits.
27
 * This is not yet implemented.
28
 */
29
 
30
#if BITS_IN_JSAMPLE != 8
31
  Sorry, this code only copes with 8-bit JSAMPLEs. /* deliberate syntax err */
32
#endif
33
 
34
/*
35
 * Since BMP stores scanlines bottom-to-top, we have to invert the image
36
 * from JPEG's top-to-bottom order.  To do this, we save the outgoing data
37
 * in a virtual array during put_pixel_row calls, then actually emit the
38
 * BMP file during finish_output.  The virtual array contains one JSAMPLE per
39
 * pixel if the output is grayscale or colormapped, three if it is full color.
40
 */
41
 
42
/* Private version of data destination object */
43
 
44
typedef struct {
45
  struct djpeg_dest_struct pub;	/* public fields */
46
 
47
  boolean is_os2;		/* saves the OS2 format request flag */
48
 
49
  jvirt_sarray_ptr whole_image;	/* needed to reverse row order */
50
  JDIMENSION data_width;	/* JSAMPLEs per row */
51
  JDIMENSION row_width;		/* physical width of one row in the BMP file */
52
  int pad_bytes;		/* number of padding bytes needed per row */
53
  JDIMENSION cur_output_row;	/* next row# to write to virtual array */
54
} bmp_dest_struct;
55
 
56
typedef bmp_dest_struct * bmp_dest_ptr;
57
 
58
 
59
/* Forward declarations */
60
LOCAL(void) write_colormap
61
	JPP((j_decompress_ptr cinfo, bmp_dest_ptr dest,
62
	     int map_colors, int map_entry_size));
63
 
64
 
65
/*
66
 * Write some pixel data.
67
 * In this module rows_supplied will always be 1.
68
 */
69
 
70
METHODDEF(void)
71
put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
72
		JDIMENSION rows_supplied)
73
/* This version is for writing 24-bit pixels */
74
{
75
  bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
76
  JSAMPARRAY image_ptr;
77
  register JSAMPROW inptr, outptr;
78
  register JDIMENSION col;
79
  int pad;
80
 
81
  /* Access next row in virtual array */
82
  image_ptr = (*cinfo->mem->access_virt_sarray)
83
    ((j_common_ptr) cinfo, dest->whole_image,
84
     dest->cur_output_row, (JDIMENSION) 1, TRUE);
85
  dest->cur_output_row++;
86
 
87
  /* Transfer data.  Note destination values must be in BGR order
88
   * (even though Microsoft's own documents say the opposite).
89
   */
90
  inptr = dest->pub.buffer[0];
91
  outptr = image_ptr[0];
92
  for (col = cinfo->output_width; col > 0; col--) {
93
    outptr[2] = *inptr++;	/* can omit GETJSAMPLE() safely */
94
    outptr[1] = *inptr++;
95
    outptr[0] = *inptr++;
96
    outptr += 3;
97
  }
98
 
99
  /* Zero out the pad bytes. */
100
  pad = dest->pad_bytes;
101
  while (--pad >= 0)
102
    *outptr++ = 0;
103
}
104
 
105
METHODDEF(void)
106
put_gray_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
107
	       JDIMENSION rows_supplied)
108
/* This version is for grayscale OR quantized color output */
109
{
110
  bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
111
  JSAMPARRAY image_ptr;
112
  register JSAMPROW inptr, outptr;
113
  register JDIMENSION col;
114
  int pad;
115
 
116
  /* Access next row in virtual array */
117
  image_ptr = (*cinfo->mem->access_virt_sarray)
118
    ((j_common_ptr) cinfo, dest->whole_image,
119
     dest->cur_output_row, (JDIMENSION) 1, TRUE);
120
  dest->cur_output_row++;
121
 
122
  /* Transfer data. */
123
  inptr = dest->pub.buffer[0];
124
  outptr = image_ptr[0];
125
  for (col = cinfo->output_width; col > 0; col--) {
126
    *outptr++ = *inptr++;	/* can omit GETJSAMPLE() safely */
127
  }
128
 
129
  /* Zero out the pad bytes. */
130
  pad = dest->pad_bytes;
131
  while (--pad >= 0)
132
    *outptr++ = 0;
133
}
134
 
135
 
136
/*
137
 * Startup: normally writes the file header.
138
 * In this module we may as well postpone everything until finish_output.
139
 */
140
 
141
METHODDEF(void)
142
start_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
143
{
144
  /* no work here */
145
}
146
 
147
 
148
/*
149
 * Finish up at the end of the file.
150
 *
151
 * Here is where we really output the BMP file.
152
 *
153
 * First, routines to write the Windows and OS/2 variants of the file header.
154
 */
155
 
156
LOCAL(void)
157
write_bmp_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
158
/* Write a Windows-style BMP file header, including colormap if needed */
159
{
160
  char bmpfileheader[14];
161
  char bmpinfoheader[40];
162
#define PUT_2B(array,offset,value)  \
163
	(array[offset] = (char) ((value) & 0xFF), \
164
	 array[offset+1] = (char) (((value) >> 8) & 0xFF))
165
#define PUT_4B(array,offset,value)  \
166
	(array[offset] = (char) ((value) & 0xFF), \
167
	 array[offset+1] = (char) (((value) >> 8) & 0xFF), \
168
	 array[offset+2] = (char) (((value) >> 16) & 0xFF), \
169
	 array[offset+3] = (char) (((value) >> 24) & 0xFF))
170
  INT32 headersize, bfSize;
171
  int bits_per_pixel, cmap_entries;
172
 
173
  /* Compute colormap size and total file size */
174
  if (cinfo->out_color_space == JCS_RGB) {
175
    if (cinfo->quantize_colors) {
176
      /* Colormapped RGB */
177
      bits_per_pixel = 8;
178
      cmap_entries = 256;
179
    } else {
180
      /* Unquantized, full color RGB */
181
      bits_per_pixel = 24;
182
      cmap_entries = 0;
183
    }
184
  } else {
185
    /* Grayscale output.  We need to fake a 256-entry colormap. */
186
    bits_per_pixel = 8;
187
    cmap_entries = 256;
188
  }
189
  /* File size */
190
  headersize = 14 + 40 + cmap_entries * 4; /* Header and colormap */
191
  bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height;
192
 
193
  /* Set unused fields of header to 0 */
194
  MEMZERO(bmpfileheader, SIZEOF(bmpfileheader));
195
  MEMZERO(bmpinfoheader, SIZEOF(bmpinfoheader));
196
 
197
  /* Fill the file header */
198
  bmpfileheader[0] = 0x42;	/* first 2 bytes are ASCII 'B', 'M' */
199
  bmpfileheader[1] = 0x4D;
200
  PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */
201
  /* we leave bfReserved1 & bfReserved2 = 0 */
202
  PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */
203
 
204
  /* Fill the info header (Microsoft calls this a BITMAPINFOHEADER) */
205
  PUT_2B(bmpinfoheader, 0, 40);	/* biSize */
206
  PUT_4B(bmpinfoheader, 4, cinfo->output_width); /* biWidth */
207
  PUT_4B(bmpinfoheader, 8, cinfo->output_height); /* biHeight */
208
  PUT_2B(bmpinfoheader, 12, 1);	/* biPlanes - must be 1 */
209
  PUT_2B(bmpinfoheader, 14, bits_per_pixel); /* biBitCount */
210
  /* we leave biCompression = 0, for none */
211
  /* we leave biSizeImage = 0; this is correct for uncompressed data */
212
  if (cinfo->density_unit == 2) { /* if have density in dots/cm, then */
213
    PUT_4B(bmpinfoheader, 24, (INT32) (cinfo->X_density*100)); /* XPels/M */
214
    PUT_4B(bmpinfoheader, 28, (INT32) (cinfo->Y_density*100)); /* XPels/M */
215
  }
216
  PUT_2B(bmpinfoheader, 32, cmap_entries); /* biClrUsed */
217
  /* we leave biClrImportant = 0 */
218
 
219
  if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14)
220
    ERREXIT(cinfo, JERR_FILE_WRITE);
221
  if (JFWRITE(dest->pub.output_file, bmpinfoheader, 40) != (size_t) 40)
222
    ERREXIT(cinfo, JERR_FILE_WRITE);
223
 
224
  if (cmap_entries > 0)
225
    write_colormap(cinfo, dest, cmap_entries, 4);
226
}
227
 
228
 
229
LOCAL(void)
230
write_os2_header (j_decompress_ptr cinfo, bmp_dest_ptr dest)
231
/* Write an OS2-style BMP file header, including colormap if needed */
232
{
233
  char bmpfileheader[14];
234
  char bmpcoreheader[12];
235
  INT32 headersize, bfSize;
236
  int bits_per_pixel, cmap_entries;
237
 
238
  /* Compute colormap size and total file size */
239
  if (cinfo->out_color_space == JCS_RGB) {
240
    if (cinfo->quantize_colors) {
241
      /* Colormapped RGB */
242
      bits_per_pixel = 8;
243
      cmap_entries = 256;
244
    } else {
245
      /* Unquantized, full color RGB */
246
      bits_per_pixel = 24;
247
      cmap_entries = 0;
248
    }
249
  } else {
250
    /* Grayscale output.  We need to fake a 256-entry colormap. */
251
    bits_per_pixel = 8;
252
    cmap_entries = 256;
253
  }
254
  /* File size */
255
  headersize = 14 + 12 + cmap_entries * 3; /* Header and colormap */
256
  bfSize = headersize + (INT32) dest->row_width * (INT32) cinfo->output_height;
257
 
258
  /* Set unused fields of header to 0 */
259
  MEMZERO(bmpfileheader, SIZEOF(bmpfileheader));
260
  MEMZERO(bmpcoreheader, SIZEOF(bmpcoreheader));
261
 
262
  /* Fill the file header */
263
  bmpfileheader[0] = 0x42;	/* first 2 bytes are ASCII 'B', 'M' */
264
  bmpfileheader[1] = 0x4D;
265
  PUT_4B(bmpfileheader, 2, bfSize); /* bfSize */
266
  /* we leave bfReserved1 & bfReserved2 = 0 */
267
  PUT_4B(bmpfileheader, 10, headersize); /* bfOffBits */
268
 
269
  /* Fill the info header (Microsoft calls this a BITMAPCOREHEADER) */
270
  PUT_2B(bmpcoreheader, 0, 12);	/* bcSize */
271
  PUT_2B(bmpcoreheader, 4, cinfo->output_width); /* bcWidth */
272
  PUT_2B(bmpcoreheader, 6, cinfo->output_height); /* bcHeight */
273
  PUT_2B(bmpcoreheader, 8, 1);	/* bcPlanes - must be 1 */
274
  PUT_2B(bmpcoreheader, 10, bits_per_pixel); /* bcBitCount */
275
 
276
  if (JFWRITE(dest->pub.output_file, bmpfileheader, 14) != (size_t) 14)
277
    ERREXIT(cinfo, JERR_FILE_WRITE);
278
  if (JFWRITE(dest->pub.output_file, bmpcoreheader, 12) != (size_t) 12)
279
    ERREXIT(cinfo, JERR_FILE_WRITE);
280
 
281
  if (cmap_entries > 0)
282
    write_colormap(cinfo, dest, cmap_entries, 3);
283
}
284
 
285
 
286
/*
287
 * Write the colormap.
288
 * Windows uses BGR0 map entries; OS/2 uses BGR entries.
289
 */
290
 
291
LOCAL(void)
292
write_colormap (j_decompress_ptr cinfo, bmp_dest_ptr dest,
293
		int map_colors, int map_entry_size)
294
{
295
  JSAMPARRAY colormap = cinfo->colormap;
296
  int num_colors = cinfo->actual_number_of_colors;
297
  FILE * outfile = dest->pub.output_file;
298
  int i;
299
 
300
  if (colormap != NULL) {
301
    if (cinfo->out_color_components == 3) {
302
      /* Normal case with RGB colormap */
303
      for (i = 0; i < num_colors; i++) {
304
	putc(GETJSAMPLE(colormap[2][i]), outfile);
305
	putc(GETJSAMPLE(colormap[1][i]), outfile);
306
	putc(GETJSAMPLE(colormap[0][i]), outfile);
307
	if (map_entry_size == 4)
308
	  putc(0, outfile);
309
      }
310
    } else {
311
      /* Grayscale colormap (only happens with grayscale quantization) */
312
      for (i = 0; i < num_colors; i++) {
313
	putc(GETJSAMPLE(colormap[0][i]), outfile);
314
	putc(GETJSAMPLE(colormap[0][i]), outfile);
315
	putc(GETJSAMPLE(colormap[0][i]), outfile);
316
	if (map_entry_size == 4)
317
	  putc(0, outfile);
318
      }
319
    }
320
  } else {
321
    /* If no colormap, must be grayscale data.  Generate a linear "map". */
322
    for (i = 0; i < 256; i++) {
323
      putc(i, outfile);
324
      putc(i, outfile);
325
      putc(i, outfile);
326
      if (map_entry_size == 4)
327
	putc(0, outfile);
328
    }
329
  }
330
  /* Pad colormap with zeros to ensure specified number of colormap entries */
331
  if (i > map_colors)
332
    ERREXIT1(cinfo, JERR_TOO_MANY_COLORS, i);
333
  for (; i < map_colors; i++) {
334
    putc(0, outfile);
335
    putc(0, outfile);
336
    putc(0, outfile);
337
    if (map_entry_size == 4)
338
      putc(0, outfile);
339
  }
340
}
341
 
342
 
343
METHODDEF(void)
344
finish_output_bmp (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
345
{
346
  bmp_dest_ptr dest = (bmp_dest_ptr) dinfo;
347
  register FILE * outfile = dest->pub.output_file;
348
  JSAMPARRAY image_ptr;
349
  register JSAMPROW data_ptr;
350
  JDIMENSION row;
351
  register JDIMENSION col;
352
  cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
353
 
354
  /* Write the header and colormap */
355
  if (dest->is_os2)
356
    write_os2_header(cinfo, dest);
357
  else
358
    write_bmp_header(cinfo, dest);
359
 
360
  /* Write the file body from our virtual array */
361
  for (row = cinfo->output_height; row > 0; row--) {
362
    if (progress != NULL) {
363
      progress->pub.pass_counter = (long) (cinfo->output_height - row);
364
      progress->pub.pass_limit = (long) cinfo->output_height;
365
      (*progress->pub.progress_monitor) ((j_common_ptr) cinfo);
366
    }
367
    image_ptr = (*cinfo->mem->access_virt_sarray)
368
      ((j_common_ptr) cinfo, dest->whole_image, row-1, (JDIMENSION) 1, FALSE);
369
    data_ptr = image_ptr[0];
370
    for (col = dest->row_width; col > 0; col--) {
371
      putc(GETJSAMPLE(*data_ptr), outfile);
372
      data_ptr++;
373
    }
374
  }
375
  if (progress != NULL)
376
    progress->completed_extra_passes++;
377
 
378
  /* Make sure we wrote the output file OK */
379
  fflush(outfile);
380
  if (ferror(outfile))
381
    ERREXIT(cinfo, JERR_FILE_WRITE);
382
}
383
 
384
 
385
/*
386
 * The module selection routine for BMP format output.
387
 */
388
 
389
GLOBAL(djpeg_dest_ptr)
390
jinit_write_bmp (j_decompress_ptr cinfo, boolean is_os2)
391
{
392
  bmp_dest_ptr dest;
393
  JDIMENSION row_width;
394
 
395
  /* Create module interface object, fill in method pointers */
396
  dest = (bmp_dest_ptr)
397
      (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
398
				  SIZEOF(bmp_dest_struct));
399
  dest->pub.start_output = start_output_bmp;
400
  dest->pub.finish_output = finish_output_bmp;
401
  dest->is_os2 = is_os2;
402
 
403
  if (cinfo->out_color_space == JCS_GRAYSCALE) {
404
    dest->pub.put_pixel_rows = put_gray_rows;
405
  } else if (cinfo->out_color_space == JCS_RGB) {
406
    if (cinfo->quantize_colors)
407
      dest->pub.put_pixel_rows = put_gray_rows;
408
    else
409
      dest->pub.put_pixel_rows = put_pixel_rows;
410
  } else {
411
    ERREXIT(cinfo, JERR_BMP_COLORSPACE);
412
  }
413
 
414
  /* Calculate output image dimensions so we can allocate space */
415
  jpeg_calc_output_dimensions(cinfo);
416
 
417
  /* Determine width of rows in the BMP file (padded to 4-byte boundary). */
418
  row_width = cinfo->output_width * cinfo->output_components;
419
  dest->data_width = row_width;
420
  while ((row_width & 3) != 0) row_width++;
421
  dest->row_width = row_width;
422
  dest->pad_bytes = (int) (row_width - dest->data_width);
423
 
424
  /* Allocate space for inversion array, prepare for write pass */
425
  dest->whole_image = (*cinfo->mem->request_virt_sarray)
426
    ((j_common_ptr) cinfo, JPOOL_IMAGE, FALSE,
427
     row_width, cinfo->output_height, (JDIMENSION) 1);
428
  dest->cur_output_row = 0;
429
  if (cinfo->progress != NULL) {
430
    cd_progress_ptr progress = (cd_progress_ptr) cinfo->progress;
431
    progress->total_extra_passes++; /* count file input as separate pass */
432
  }
433
 
434
  /* Create decompressor output buffer. */
435
  dest->pub.buffer = (*cinfo->mem->alloc_sarray)
436
    ((j_common_ptr) cinfo, JPOOL_IMAGE, row_width, (JDIMENSION) 1);
437
  dest->pub.buffer_height = 1;
438
 
439
  return (djpeg_dest_ptr) dest;
440
}
441
 
442
#endif /* BMP_SUPPORTED */