| 1 | #ifndef lint | 
| 2 | static const char RCSid[] = "$Id: abitmapio.cpp,v 2.2 2024/08/24 23:25:24 greg Exp $"; | 
| 3 | #endif | 
| 4 | /* | 
| 5 | *  abitmapio.cpp | 
| 6 | * | 
| 7 | *  BitMap class file i/o using BMP format. | 
| 8 | * | 
| 9 | *  Created by Greg Ward on 6/30/16. | 
| 10 | */ | 
| 11 |  | 
| 12 | #include <stdio.h> | 
| 13 | #include <stdlib.h> | 
| 14 | #include <string.h> | 
| 15 | #include "abitmap.h" | 
| 16 | #include "bmpfile.h" | 
| 17 | #include "dmessage.h" | 
| 18 |  | 
| 19 | static const char       BitMap1Dmagic[] = "1-D BitMap"; | 
| 20 | static const char       BitMap2Dmagic[] = "2-D BitMap"; | 
| 21 |  | 
| 22 | // Function to write a bitmap to a BMP file | 
| 23 | bool | 
| 24 | WriteBitMap(const ABitMap &bm, const char *fname) | 
| 25 | { | 
| 26 | if (!bm.Length() || fname == NULL || !*fname) | 
| 27 | return false; | 
| 28 | if (bm.Length() >= 1L<<31) { | 
| 29 | DMESG(DMCparameter, "BitMap too long to write as one scanline"); | 
| 30 | return false; | 
| 31 | } | 
| 32 | BMPHeader *     hdr = BMPmappedHeader(bm.Length(), 1, 16, 2); | 
| 33 | if (hdr == NULL) { | 
| 34 | DMESG(DMCparameter, "Cannot create BMP header"); | 
| 35 | return false; | 
| 36 | } | 
| 37 | strncpy(BMPinfo(hdr), BitMap1Dmagic, 16); | 
| 38 |  | 
| 39 | BMPWriter *     wtr = BMPopenOutputFile(fname, hdr); | 
| 40 | if (wtr == NULL) { | 
| 41 | DMESGF(DMCresource, "Cannot open BMP output file '%s'", fname); | 
| 42 | free(hdr); | 
| 43 | return false; | 
| 44 | } | 
| 45 | DMESGF(DMCtrace, "Writing 1-D bitmap to '%s'", fname); | 
| 46 |  | 
| 47 | memset(wtr->scanline, 0, (bm.Length()+7)>>3); | 
| 48 | for (uint32 i = 0; bm.Find(&i); i++) | 
| 49 | wtr->scanline[i>>3] |= 128>>(i&7); | 
| 50 |  | 
| 51 | int     rval = BMPwriteScanline(wtr); | 
| 52 |  | 
| 53 | BMPcloseOutput(wtr); | 
| 54 |  | 
| 55 | if (rval != BIR_OK) { | 
| 56 | DMESG(DMCdata, BMPerrorMessage(rval)); | 
| 57 | return false; | 
| 58 | } | 
| 59 | return true; | 
| 60 | } | 
| 61 |  | 
| 62 | // Function to write a 2-D bitmap to a BMP file | 
| 63 | bool | 
| 64 | WriteBitMap2(const ABitMap2 &bm2, const char *fname) | 
| 65 | { | 
| 66 | if (!bm2.Width() | !bm2.Height() || fname == NULL || !*fname) | 
| 67 | return false; | 
| 68 |  | 
| 69 | BMPHeader *     hdr = BMPmappedHeader(bm2.Width(), bm2.Height(), 16, 2); | 
| 70 | if (hdr == NULL) { | 
| 71 | DMESG(DMCparameter, "Cannot create BMP header"); | 
| 72 | return false; | 
| 73 | } | 
| 74 | strncpy(BMPinfo(hdr), BitMap2Dmagic, 16); | 
| 75 | hdr->yIsDown = 1;               // sane scanline ordering | 
| 76 |  | 
| 77 | BMPWriter *     wtr = BMPopenOutputFile(fname, hdr); | 
| 78 | if (wtr == NULL) { | 
| 79 | DMESGF(DMCresource, "Cannot open BMP output file '%s'", fname); | 
| 80 | free(hdr); | 
| 81 | return false; | 
| 82 | } | 
| 83 | DMESGF(DMCtrace, "Writing 2-D bitmap to '%s'", fname); | 
| 84 | int     rval = BIR_OK; | 
| 85 | int     my = 0; | 
| 86 | for (int y = 0; y < bm2.Height(); y++) { | 
| 87 | memset(wtr->scanline, 0, (bm2.Width()+7)>>3); | 
| 88 | while (y < my) {        // write empty scanlines | 
| 89 | if ((rval = BMPwriteScanline(wtr)) != BIR_OK) | 
| 90 | break; | 
| 91 | ++y; | 
| 92 | } | 
| 93 | if ((rval != BIR_OK) | (y >= bm2.Height())) | 
| 94 | break; | 
| 95 | for (int x = 0; bm2.Find(&x, &my) && my == y; x++) | 
| 96 | wtr->scanline[x>>3] |= 128>>(x&7); | 
| 97 |  | 
| 98 | if ((rval = BMPwriteScanline(wtr)) != BIR_OK) | 
| 99 | break; | 
| 100 | } | 
| 101 | BMPcloseOutput(wtr); | 
| 102 |  | 
| 103 | if (rval != BIR_OK) { | 
| 104 | DMESG(DMCdata, BMPerrorMessage(rval)); | 
| 105 | return false; | 
| 106 | } | 
| 107 | return true; | 
| 108 | } | 
| 109 |  | 
| 110 | // Function to read a bitmap from a BMP file | 
| 111 | bool | 
| 112 | ReadBitMap(ABitMap *bmp, const char *fname) | 
| 113 | { | 
| 114 | if (bmp == NULL || fname == NULL || !*fname) | 
| 115 | return false; | 
| 116 | // open call reads 1st scanline | 
| 117 | BMPReader *     rdr = BMPopenInputFile(fname); | 
| 118 |  | 
| 119 | if (rdr == NULL) { | 
| 120 | DMESGF(DMCresource, "Cannot open BMP input file '%s'", fname); | 
| 121 | return false; | 
| 122 | } | 
| 123 | if (rdr->hdr->nColors > 2) { | 
| 124 | DMESGF(DMCdata, "BMP input file '%s' is not a bitmap", fname); | 
| 125 | BMPcloseInput(rdr); | 
| 126 | return false; | 
| 127 | } | 
| 128 | if (rdr->hdr->height != 1) { | 
| 129 | DMESGF(DMCdata, "BMP input file '%s' has more than one scan line", fname); | 
| 130 | BMPcloseInput(rdr); | 
| 131 | return false; | 
| 132 | } | 
| 133 | if (rdr->hdr->infoSiz <= 0 || | 
| 134 | strncmp(BMPinfo(rdr->hdr), BitMap1Dmagic, rdr->hdr->infoSiz)) | 
| 135 | DMESGF(DMCwarning, "BMP file '%s' not flagged as 1-D bitmap", fname); | 
| 136 |  | 
| 137 | DASSERT(rdr->yscan == 0); | 
| 138 | DMESGF(DMCtrace, "Reading 1-D bitmap from '%s'", fname); | 
| 139 | bmp->NewBitMap(rdr->hdr->width); | 
| 140 | for (uint32 i = rdr->hdr->width; i--; ) | 
| 141 | if (rdr->scanline[i>>3] & 128>>(i&7)) | 
| 142 | bmp->Set(i); | 
| 143 | if (rdr->hdr->palette[0].g >= 128) | 
| 144 | bmp->Invert();          // against normal convention! | 
| 145 | BMPcloseInput(rdr); | 
| 146 | return true; | 
| 147 | } | 
| 148 |  | 
| 149 | // Function to read a 2-D bitmap from a BMP file | 
| 150 | bool | 
| 151 | ReadBitMap2(ABitMap2 *bm2p, const char *fname) | 
| 152 | { | 
| 153 | if (bm2p == NULL || fname == NULL || !*fname) | 
| 154 | return false; | 
| 155 |  | 
| 156 | BMPReader *     rdr = BMPopenInputFile(fname); | 
| 157 |  | 
| 158 | if (rdr == NULL) { | 
| 159 | DMESGF(DMCresource, "Cannot open BMP input file '%s'", fname); | 
| 160 | return false; | 
| 161 | } | 
| 162 | if (rdr->hdr->nColors > 2) { | 
| 163 | DMESGF(DMCdata, "BMP input file '%s' is not a bitmap", fname); | 
| 164 | BMPcloseInput(rdr); | 
| 165 | return false; | 
| 166 | } | 
| 167 | if (rdr->hdr->infoSiz <= 0 || | 
| 168 | strncmp(BMPinfo(rdr->hdr), BitMap2Dmagic, rdr->hdr->infoSiz)) | 
| 169 | DMESGF(DMCwarning, "BMP file '%s' not flagged as 2-D bitmap", fname); | 
| 170 |  | 
| 171 | int     y, rval = BIR_OK; | 
| 172 | bm2p->NewBitMap(rdr->hdr->width, rdr->hdr->height); | 
| 173 | DMESGF(DMCtrace, "Reading 2-D bitmap from '%s'", fname); | 
| 174 | for (y = 0; y < rdr->hdr->height; y++) { | 
| 175 | const int       my = rdr->hdr->yIsDown ? y | 
| 176 | : rdr->hdr->height-1 - y; | 
| 177 | while (rdr->yscan < y && (rval = BMPreadScanline(rdr)) == BIR_OK) | 
| 178 | ; | 
| 179 | if (rval != BIR_OK) | 
| 180 | break; | 
| 181 | for (uint32 x = rdr->hdr->width; x--; ) | 
| 182 | if (rdr->scanline[x>>3] & 128>>(x&7)) | 
| 183 | bm2p->Set(x, my); | 
| 184 | } | 
| 185 | if (rval == BIR_OK && rdr->hdr->palette[0].g >= 128) | 
| 186 | bm2p->Invert();         // against normal convention! | 
| 187 | BMPcloseInput(rdr); | 
| 188 |  | 
| 189 | if (rval != BIR_OK) { | 
| 190 | bm2p->NewBitMap(0,0); | 
| 191 | sprintf(dmessage_buf, "%s at y==%d", BMPerrorMessage(rval), y); | 
| 192 | DMESG(DMCdata, dmessage_buf); | 
| 193 | return false; | 
| 194 | } | 
| 195 | return true; | 
| 196 | } |