// // image_to_c - convert binary image files into c-compatible data tables // // Written by Larry Bank // Copyright (c) 2020 BitBank Software, Inc. // Change history // 12/2/20 - Started the project // #include #include #include #include #define TEMP_BUF_SIZE 4096 #define DEFAULT_READ_SIZE 256 #define MAX_TAGS 256 #define TIFF_TAGSIZE 12 #define INTELSHORT(p) ((*p) + (*(p+1)<<8)) #define INTELLONG(p) ((*p) + (*(p+1)<<8) + (*(p+2)<<16) + (*(p+3)<<24)) #define MOTOSHORT(p) (((*(p))<<8) + (*(p+1))) #define MOTOLONG(p) (((*p)<<24) + ((*(p+1))<<16) + ((*(p+2))<<8) + (*(p+3))) #ifdef _WIN32 #define PILIO_SLASH_CHAR '\\' #else #define PILIO_SLASH_CHAR '/' #endif typedef unsigned char BOOL; const char *szType[] = {"Unknown", "PNG","JFIF","Win BMP","OS/2 BMP","TIFF","GIF","Portable Pixmap","Targa","JEDMICS","CALS","PCX"}; const char *szComp[] = {"Unknown", "Flate","JPEG","None","RLE","LZW","G3","G4","Packbits","Modified Huffman","Thunderscan RLE","JBIG (T.85)"}; const char *szPhotometric[] = {"WhiteIsZero","BlackIsZero","RGB","Palette Color","Transparency Mask","CMYK","YCbCr","Unknown"}; const char *szPlanar[] = {"Unknown","Chunky","Planar"}; enum { FILETYPE_UNKNOWN = 0, FILETYPE_PNG, FILETYPE_JPEG, FILETYPE_BMP, FILETYPE_OS2BMP, FILETYPE_TIFF, FILETYPE_GIF, FILETYPE_PPM, FILETYPE_TARGA, FILETYPE_JEDMICS, FILETYPE_CALS, FILETYPE_PCX }; enum { COMPTYPE_UNKNOWN = 0, COMPTYPE_FLATE, COMPTYPE_JPEG, COMPTYPE_NONE, COMPTYPE_RLE, COMPTYPE_LZW, COMPTYPE_G3, COMPTYPE_G4, COMPTYPE_PACKBITS, COMPTYPE_HUFFMAN, COMPTYPE_THUNDERSCAN, COMPTYPE_JBIG }; FILE * ihandle; void MakeC(unsigned char *, int, int); void GetLeafName(char *fname, char *leaf); void FixName(char *name); unsigned short TIFFSHORT(unsigned char *p, BOOL bMotorola) { unsigned short s; if (bMotorola) s = *p * 0x100 + *(p+1); else s = *p + *(p+1)*0x100; return s; } /* TIFFSHORT() */ uint32_t TIFFLONG(unsigned char *p, BOOL bMotorola) { uint32_t l; if (bMotorola) l = *p * 0x1000000 + *(p+1) * 0x10000 + *(p+2) * 0x100 + *(p+3); else l = *p + *(p+1) * 0x100 + *(p+2) * 0x10000 + *(p+3) * 0x1000000; return l; } /* TIFFLONG() */ int TIFFVALUE(unsigned char *p, BOOL bMotorola) { int i, iType; iType = TIFFSHORT(p+2, bMotorola); /* If pointer to a list of items, must be a long */ if (TIFFSHORT(p+4, bMotorola) > 1) iType = 4; switch (iType) { case 3: /* Short */ i = TIFFSHORT(p+8, bMotorola); break; case 4: /* Long */ case 7: // undefined (treat it as a long since it's usually a multibyte buffer) i = TIFFLONG(p+8, bMotorola); break; case 6: // signed byte i = (signed char)p[8]; break; case 2: /* ASCII */ case 5: /* Unsigned Rational */ case 10: /* Signed Rational */ i = TIFFLONG(p+8, bMotorola); break; default: /* to suppress compiler warning */ i = 0; break; } return i; } /* TIFFVALUE() */ int ParseNumber(unsigned char *buf, int *iOff, int iLength) { int i, iOffset; i = 0; iOffset = *iOff; while (iOffset < iLength && buf[iOffset] >= '0' && buf[iOffset] <= '9') { i *= 10; i += (int)(buf[iOffset++] - '0'); } *iOff = iOffset+1; /* Skip ending char */ return i; } /* ParseNumber() */ // // CountGIFFrames // // Given a pointer to a multipage GIF image // This function will walk through the file and count the number // of frames present // int CountGIFFrames(unsigned char *cBuf, int iFileSize) { int iNumFrames; size_t iOff; int bDone = 0; int bExt; unsigned char c; iNumFrames = 0; iOff = 10; c = cBuf[iOff]; // get info bits iOff += 3; /* Skip flags, background color & aspect ratio */ if (c & 0x80) /* Deal with global color table */ { c &= 7; /* Get the number of colors defined */ iOff += (2<= iFileSize) // problem { iNumFrames--; // possible corrupt data; stop bDone = 1; // *bTruncated = TRUE; continue; } /* Start of image data */ c = cBuf[iOff+9]; /* Get the flags byte */ iOff += 10; /* Skip image position and size */ if (c & 0x80) /* Local color table */ { c &= 7; iOff += (2< iFileSize) // past end of file, stop { iNumFrames--; // don't count this frame break; // last page is corrupted, don't use it } c = cBuf[iOff++]; /* Get length of next */ } /* End of image data, check for more frames... */ if ((iOff > iFileSize) || cBuf[iOff] == 0x3b) { bDone = 1; /* End of file has been reached */ } else { iNumFrames++; } } /* while !bDone */ return iNumFrames; } /* CountGIFFrames() */ // // Returns the image data size // int ImageInfo(FILE *iHandle, int iFileSize, char *szInfo, int *iDataOff) { int i, j, k; int iBytes; int iFileType = FILETYPE_UNKNOWN; int iCompression = COMPTYPE_UNKNOWN; unsigned char cBuf[TEMP_BUF_SIZE]; // small buffer to load header info int iBpp = 0; int iWidth = 0; int iHeight = 0; int iOffset; int iMarker; int iPhotoMetric; int iPlanar; int iCount; int iDataSize = 0; // size of the compressed data unsigned char ucSubSample; BOOL bMotorola; char szOptions[256]; szInfo[0] = 0; // start with null string // Detect the file type by its header iBytes = fread(cBuf, 1, DEFAULT_READ_SIZE, iHandle); if (iBytes < 64) return -1; // too small if (MOTOLONG(cBuf) == 0x89504e47) // PNG iFileType = FILETYPE_PNG; else if (cBuf[0] == 'B' && cBuf[1] == 'M') // BMP { if (cBuf[14] == 0x28) // Windows iFileType = FILETYPE_BMP; else iFileType = FILETYPE_OS2BMP; } else if (cBuf[0] == 0x0a && cBuf[1] < 0x6 && cBuf[2] == 0x01) { iFileType = FILETYPE_PCX; } else if (INTELLONG(cBuf) == 0x80 && (cBuf[36] == 4 || cBuf[36] == 6)) { iFileType = FILETYPE_JEDMICS; } else if (INTELLONG(cBuf) == 0x64637273) { iFileType = FILETYPE_CALS; } else if ((MOTOLONG(cBuf) & 0xffffff00) == 0xffd8ff00) // JPEG iFileType = FILETYPE_JPEG; else if (MOTOLONG(cBuf) == 0x47494638 /*'GIF8'*/) // GIF iFileType = FILETYPE_GIF; else if ((cBuf[0] == 'I' && cBuf[1] == 'I') || (cBuf[0] == 'M' && cBuf[1] == 'M')) iFileType = FILETYPE_TIFF; else { i = MOTOLONG(cBuf) & 0xffff8080; if (i == 0x50360000 || i == 0x50350000 || i == 0x50340000) // Portable bitmap/graymap/pixmap iFileType = FILETYPE_PPM; } // Check for Truvision Targa i = cBuf[1] & 0xfe; j = cBuf[2]; // make sure it is not a MPEG file (starts with 00 00 01 BA) if (MOTOLONG(cBuf) != 0x1ba && MOTOLONG(cBuf) != 0x1b3 && i == 0 && (j == 1 || j == 2 || j == 3 || j == 9 || j == 10 || j == 11)) iFileType = FILETYPE_TARGA; if (iFileType == FILETYPE_UNKNOWN) { return -1; } szOptions[0] = '\0'; // info specific to each file type // Get info specific to each type of file switch (iFileType) { case FILETYPE_PCX: iWidth = 1 + INTELSHORT(&cBuf[8]) - INTELSHORT(&cBuf[4]); iHeight = 1 + INTELSHORT(&cBuf[10]) - INTELSHORT(&cBuf[6]); iCompression = COMPTYPE_PACKBITS; iBpp = cBuf[3] * cBuf[65]; break; case FILETYPE_PNG: if (MOTOLONG(&cBuf[12]) == 0x49484452/*'IHDR'*/) { iWidth = MOTOLONG(&cBuf[16]); iHeight = MOTOLONG(&cBuf[20]); iCompression = COMPTYPE_FLATE; i = cBuf[24]; // bits per pixel j = cBuf[25]; // pixel type switch (j) { case 0: // grayscale case 3: // palette image iBpp = i; break; case 2: // RGB triple iBpp = i * 3; break; case 4: // grayscale + alpha channel iBpp = i * 2; break; case 6: // RGB + alpha iBpp = i * 4; break; } if (cBuf[28] == 1) // interlace flag strcpy(szOptions, ", Interlaced"); else strcpy(szOptions, ", Not interlaced"); } break; case FILETYPE_TARGA: iWidth = INTELSHORT(&cBuf[12]); iHeight = INTELSHORT(&cBuf[14]); iBpp = cBuf[16]; if (cBuf[2] == 3 || cBuf[2] == 11) // monochrome iBpp = 1; if (cBuf[2] < 9) iCompression = COMPTYPE_NONE; else iCompression = COMPTYPE_RLE; break; case FILETYPE_PPM: if (cBuf[1] == '4') iBpp = 1; else if (cBuf[1] == '5') iBpp = 8; else if (cBuf[1] == '6') iBpp = 24; j = 2; while ((cBuf[j] == 0xa || cBuf[j] == 0xd) && j>4),(ucSubSample & 0xf)); } break; case FILETYPE_GIF: iCompression = COMPTYPE_LZW; iWidth = INTELSHORT(&cBuf[6]); iHeight = INTELSHORT(&cBuf[8]); iBpp = (cBuf[10] & 7) + 1; if (cBuf[10] & 64) // interlace flag strcpy(szOptions, ", Interlaced"); else strcpy(szOptions, ", Not interlaced"); break; case FILETYPE_TIFF: bMotorola = (cBuf[0] == 'M'); // determine endianness of TIFF data i = TIFFLONG(&cBuf[4], bMotorola); // get first IFD offset fseek(iHandle, i, SEEK_SET);// read the entire tag directory iBytes = fread(cBuf, 1, MAX_TAGS*TIFF_TAGSIZE, iHandle); j = TIFFSHORT(cBuf, bMotorola); // get the tag count iOffset = 2; // point to start of TIFF tag directory // Some TIFF files don't specify everything, so set up some default values iBpp = 1; iPlanar = 1; iCompression = COMPTYPE_NONE; iPhotoMetric = 7; // if not specified, set to "unknown" // Each TIFF tag is made up of 12 bytes // byte 0-1: Tag value (short) // byte 2-3: data type (short) // byte 4-7: number of values (long) // byte 8-11: value or offset to list of values for (i=0; i 6) iPhotoMetric = 7; // unknown break; case 273: // strip offsets *iDataOff = TIFFVALUE(&cBuf[iOffset], bMotorola); break; case 279: // strip byte counts iDataSize = TIFFVALUE(&cBuf[iOffset], bMotorola); break; case 284: // planar/chunky iPlanar = TIFFVALUE(&cBuf[iOffset], bMotorola); if (iPlanar < 1 || iPlanar > 2) // unknown value iPlanar = 0; // unknown break; } // switch on tiff tag iOffset += TIFF_TAGSIZE; } // for each tag // sprintf(szOptions, ", Photometric = %s, Planar config = %s", szPhotometric[iPhotoMetric], szPlanar[iPlanar]); // break; } // switch sprintf(szInfo, "// %s, Compression=%s, Size: %d x %d, %d-Bpp\n", szType[iFileType], szComp[iCompression], iWidth, iHeight, iBpp); if (iFileType == FILETYPE_GIF) // see how many frames it has { // slight hack - load the file into memory uint8_t *pFile; char szTemp[32]; int iFrames; pFile = malloc(iFileSize); if (pFile != NULL) { fseek(iHandle, 0, SEEK_SET); fread(pFile, 1, iFileSize, iHandle); iFrames = CountGIFFrames(pFile, iFileSize); free(pFile); sprintf(szTemp, "// %d frames\n//\n", iFrames); strcat(szInfo, szTemp); } } else { strcat(szInfo, "//\n"); // simple end of comment } return iDataSize; } /* ImageInfo() */ // // Main program entry point // int main(int argc, char *argv[]) { int iSize, iData; int bStrip = 0; unsigned char *p; int iStart = 1; // starting parameter for input name int iDataOff, iDataSize; char szLeaf[256]; char szInfo[256]; if (argc != 2 && argc != 3) { printf("image_to_c Copyright (c) 2020 BitBank Software, Inc.\n"); printf("Written by Larry Bank\n\n"); printf("Usage: image_to_c