/* Copyright (C) 1998, 1999 artofcode LLC. All rights reserved. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA, 02111-1307. */ /*$Id: gdevbmpc.c,v 1.2.6.2.2.1 2003/01/17 00:49:00 giles Exp $ */ /* .BMP file format driver utilities */ #include "gdevprn.h" #include "gdevbmp.h" /* * Define BMP file format structures. * All multi-byte quantities are stored LSB-first! */ typedef ushort word; #if arch_sizeof_int == 4 typedef uint dword; #else # if arch_sizeof_long == 4 typedef ulong dword; # endif #endif #if arch_is_big_endian # define BMP_ASSIGN_WORD(a,v) a = ((v) >> 8) + ((v) << 8) # define BMP_ASSIGN_DWORD(a,v)\ a = ((v) >> 24) + (((v) >> 8) & 0xff00L) +\ (((dword)(v) << 8) & 0xff0000L) + ((dword)(v) << 24) #else # define BMP_ASSIGN_WORD(a,v) a = (v) # define BMP_ASSIGN_DWORD(a,v) a = (v) #endif typedef struct bmp_file_header_s { /* BITMAPFILEHEADER */ /* * This structure actually begins with two bytes * containing the characters 'BM', but we must omit them, * because some compilers would insert padding to force * the size member to a 32- or 64-bit boundary. */ /*byte typeB, typeM; *//* always 'BM' */ dword size; /* total size of file */ word reserved1; word reserved2; dword offBits; /* offset of bits from start of file */ } bmp_file_header; #define sizeof_bmp_file_header (2 + sizeof(bmp_file_header)) typedef struct bmp_info_header_s { /* BITMAPINFOHEADER */ dword size; /* size of info header in bytes */ dword width; /* width in pixels */ dword height; /* height in pixels */ word planes; /* # of planes, always 1 */ word bitCount; /* bits per pixel */ dword compression; /* compression scheme, always 0 */ dword sizeImage; /* size of bits */ dword xPelsPerMeter; /* X pixels per meter */ dword yPelsPerMeter; /* Y pixels per meter */ dword clrUsed; /* # of colors used */ dword clrImportant; /* # of important colors */ /* This is followed by (1 << bitCount) bmp_quad structures, */ /* unless bitCount == 24. */ } bmp_info_header; typedef struct bmp_quad_s { /* RGBQUAD */ byte blue, green, red, reserved; } bmp_quad; /* Write the BMP file header. */ private int write_bmp_depth_header(gx_device_printer *pdev, FILE *file, int depth, const byte *palette /* [4 << depth] */, int raster) { /* BMP scan lines are padded to 32 bits. */ ulong bmp_raster = raster + (-raster & 3); int height = pdev->height; int quads = (depth <= 8 ? sizeof(bmp_quad) << depth : 0); /* Write the file header. */ fputc('B', file); fputc('M', file); { bmp_file_header fhdr; BMP_ASSIGN_DWORD(fhdr.size, sizeof_bmp_file_header + sizeof(bmp_info_header) + quads + bmp_raster * height); BMP_ASSIGN_WORD(fhdr.reserved1, 0); BMP_ASSIGN_WORD(fhdr.reserved2, 0); BMP_ASSIGN_DWORD(fhdr.offBits, sizeof_bmp_file_header + sizeof(bmp_info_header) + quads); if (fwrite((const char *)&fhdr, 1, sizeof(fhdr), file) != sizeof(fhdr)) return_error(gs_error_ioerror); } /* Write the info header. */ { bmp_info_header ihdr; BMP_ASSIGN_DWORD(ihdr.size, sizeof(ihdr)); BMP_ASSIGN_DWORD(ihdr.width, pdev->width); BMP_ASSIGN_DWORD(ihdr.height, height); BMP_ASSIGN_WORD(ihdr.planes, 1); BMP_ASSIGN_WORD(ihdr.bitCount, depth); BMP_ASSIGN_DWORD(ihdr.compression, 0); BMP_ASSIGN_DWORD(ihdr.sizeImage, bmp_raster * height); /* * Earlier versions of this driver set the PelsPerMeter values * to zero. At a user's request, we now set them correctly, * but we suspect this will cause problems other places. */ #define INCHES_PER_METER (100 /*cm/meter*/ / 2.54 /*cm/inch*/) BMP_ASSIGN_DWORD(ihdr.xPelsPerMeter, (dword)(pdev->x_pixels_per_inch * INCHES_PER_METER + 0.5)); BMP_ASSIGN_DWORD(ihdr.yPelsPerMeter, (dword)(pdev->y_pixels_per_inch * INCHES_PER_METER + 0.5)); #undef INCHES_PER_METER BMP_ASSIGN_DWORD(ihdr.clrUsed, 0); BMP_ASSIGN_DWORD(ihdr.clrImportant, 0); if (fwrite((const char *)&ihdr, 1, sizeof(ihdr), file) != sizeof(ihdr)) return_error(gs_error_ioerror); } /* Write the palette. */ if (depth <= 8) fwrite(palette, sizeof(bmp_quad), 1 << depth, file); return 0; } /* Write the BMP file header. */ int write_bmp_header(gx_device_printer *pdev, FILE *file) { int depth = pdev->color_info.depth; bmp_quad palette[256]; if (depth <= 8) { int i; gx_color_value rgb[3]; bmp_quad q; q.reserved = 0; for (i = 0; i != 1 << depth; i++) { (*dev_proc(pdev, map_color_rgb))((gx_device *)pdev, (gx_color_index)i, rgb); q.red = gx_color_value_to_byte(rgb[0]); q.green = gx_color_value_to_byte(rgb[1]); q.blue = gx_color_value_to_byte(rgb[2]); palette[i] = q; } } return write_bmp_depth_header(pdev, file, depth, (const byte *)palette, gdev_prn_raster(pdev)); } /* Write a BMP header for separated CMYK output. */ int write_bmp_separated_header(gx_device_printer *pdev, FILE *file) { int depth = pdev->color_info.depth; int plane_depth = depth / 4; bmp_quad palette[256]; bmp_quad q; int i; q.reserved = 0; for (i = 0; i < 1 << plane_depth; i++) { q.red = q.green = q.blue = 255 - i * 255 / ((1 << plane_depth) - 1); palette[i] = q; } return write_bmp_depth_header(pdev, file, plane_depth, (const byte *)palette, bitmap_raster(pdev->width * plane_depth)); } /* 24-bit color mappers (taken from gdevmem2.c). */ /* Note that Windows expects RGB values in the order B,G,R. */ /* Map a r-g-b color to a color index. */ gx_color_index bmp_map_16m_rgb_color(gx_device * dev, gx_color_value r, gx_color_value g, gx_color_value b) { return gx_color_value_to_byte(r) + ((uint) gx_color_value_to_byte(g) << 8) + ((ulong) gx_color_value_to_byte(b) << 16); } /* Map a color index to a r-g-b color. */ int bmp_map_16m_color_rgb(gx_device * dev, gx_color_index color, gx_color_value prgb[3]) { prgb[2] = gx_color_value_from_byte(color >> 16); prgb[1] = gx_color_value_from_byte((color >> 8) & 0xff); prgb[0] = gx_color_value_from_byte(color & 0xff); return 0; }