| 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 | 
} |