Guest User

mkconvert

a guest
Nov 5th, 2012
397
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 11.10 KB | None | 0 0
  1. /*
  2.  * mkconvert
  3.  * =========
  4.  *
  5.  * Written 2012 by Magnus Kalkuhl
  6.  *
  7.  * http://twitter.com/magnuskalkuhl
  8.  *
  9.  * For "About", "Important to know", "Disclaimer" and
  10.  * "Redistribution" rules please check the text in the main() section.
  11.  *
  12.  * COMPILATION (ON UBUNTU LINUX)
  13.  * -----------------------------
  14.  *
  15.  * - Install required GD library: sudo apt-get install libgd2-xpm-dev
  16.  * - Compile with: gcc -std=c99 mkconvert.c -o mkconvert -lgd
  17.  *
  18.  * USAGE
  19.  * -----
  20.  *
  21.  * ./mkconvert <source directory> <target directory>
  22.  *
  23.  * USEFUL LINKS
  24.  * ------------
  25.  *
  26.  * - Libre office extension to export slides as PNG files:
  27.  *   http://extensions.libreoffice.org/extension-center/export-as-images
  28.  *   Ensure you disable antialiasing in Libre Office
  29.  *   (Tools -> Options -> LibreOffice -> View)
  30.  *   or text edges will look fuzzy.
  31.  *
  32.  * - Original mkviewer and the presentation it was used for:
  33.  *   http://t.co/csih7QtO
  34.  *
  35.  * - Editor for C64 disk images: http://www.d64editor.com
  36.  *   Windows only :(
  37.  *
  38. */
  39.  
  40. #include <stdio.h>
  41. #include <gd.h>
  42. #include <dirent.h>
  43. #include <string.h>
  44.  
  45. #define WIDTH       320
  46. #define HEIGHT      200
  47. #define PATH_MAX    4096
  48.  
  49. int grayscaleImage[WIDTH*HEIGHT];
  50. int c64bitmap[(WIDTH*HEIGHT)/8];
  51.  
  52. int importFileAsGrayscale(char *sourceName)
  53.     {
  54.     // Read PNG file and turn colors into gray scale luminance
  55.     // (range from black to white: 0 - 255)
  56.    
  57.     gdImagePtr im;
  58.  
  59.     FILE *in;
  60.     in = fopen(sourceName, "rb");
  61.     if (in == NULL)
  62.         {
  63.         printf("Failed to open image '%s'!\n",sourceName);
  64.         return -1;
  65.         }
  66.     im = gdImageCreateFromPng(in);
  67.     fclose(in);
  68.    
  69.     if (gdImageSX(im) != WIDTH || gdImageSY(im) != HEIGHT)
  70.         {
  71.         printf("Wrong size of '%s' (should be %i x %i, but has %i x %i)\n",
  72.         sourceName,WIDTH,HEIGHT, gdImageSX(im),gdImageSY(im));
  73.         return -1;
  74.         }
  75.  
  76.     // Import image
  77.     for (int y = 0; y < HEIGHT; y++)
  78.         {
  79.         for (int x = 0; x < WIDTH; x++)
  80.             {
  81.             int c;
  82.             c = gdImageGetPixel(im, x, y);
  83.             grayscaleImage[y*WIDTH+x] = (gdImageRed(im, c)+gdImageGreen(im, c)+gdImageBlue(im, c))/3;
  84.             }
  85.         }
  86.     gdImageDestroy(im);
  87.     return 0;
  88.     }
  89.  
  90. void ditherGrayScaleIntoBlackAndWhite()
  91.     {
  92.     // Dither grayscale image into black (0) and white (255) pixels
  93.    
  94.     for (int y = 0; y < HEIGHT; y++)
  95.         {
  96.         for (int x = 0; x < WIDTH; x++)
  97.             {
  98.             int cMix = grayscaleImage[y*WIDTH+x];
  99.             int cNew;
  100.            
  101.             int level = 0;
  102.             if (cMix > 100) level = 1;
  103.             if (cMix > 120) level = 2;
  104.             if (cMix > 140) level = 3;
  105.             if (cMix > 160) level = 4;
  106.            
  107.             if (level == 0) cNew = 0;
  108.  
  109.             if (level == 1)
  110.                 {
  111.                 cNew = 0;
  112.                 if ((x & 1) == 0 && (y & 1) == 0) cNew = 255;  
  113.                 }
  114.                
  115.             if (level == 2)
  116.                 {
  117.                 cNew = 0;
  118.                 if (((x+y) & 1) == 0) cNew = 255;  
  119.                 }          
  120.            
  121.             if (level == 3)
  122.                 {          
  123.                 cNew = 255;
  124.                 if ((x & 1) == 1 && (y & 1) == 0) cNew = 0;
  125.                 }
  126.                
  127.             if (level == 4) cNew = 255;            
  128.            
  129.             grayscaleImage[y*WIDTH+x] = cNew;
  130.             }
  131.         }
  132.        
  133.     }
  134.  
  135. void convertBlackWhiteImageIntoC64Bitmap()
  136.     {
  137.     // Convert black/white image into real C64 bitmap
  138.    
  139.     /* Some words about C64 bitmaps:
  140.      * Each byte contains 8 pixel (from left to right).
  141.      * After each byte, line increases by one.
  142.      * After eight bytes, line decreases by 8 and column increases by 8
  143.      *
  144.      * See below:
  145.      *
  146.      * BYTE  PIXELS ON SCREEN
  147.      * 00    Line 0, Columns 0-7
  148.      * 01    Line 1, Columns 0-7
  149.      * 02    Line 2, Columns 0-7
  150.      * 03    Line 3, Columns 0-7
  151.      * 04    Line 4, Columns 0-7
  152.      * 05    Line 5, Columns 0-7
  153.      * 06    Line 6, Columns 0-7
  154.      * 07    Line 7, Columns 0-7
  155.      * 08    Line 0, Columns 8-15
  156.      * 09    Line 1, Columns 8-15
  157.      * 10    Line 2, Columns 8-15
  158.      * 11    Line 3, Columns 8-15
  159.      * ...
  160.      */
  161.      
  162.     int xc = 0;
  163.     int yc = 0;
  164.     int subLine = 0;
  165.     for (int i = 0; i < ((WIDTH*HEIGHT)/8); i++)
  166.         {
  167.         c64bitmap[i] =
  168.                 ((grayscaleImage[(yc+subLine)*WIDTH+xc+0] & 1) << 7)
  169.               + ((grayscaleImage[(yc+subLine)*WIDTH+xc+1] & 1) << 6)
  170.               + ((grayscaleImage[(yc+subLine)*WIDTH+xc+2] & 1) << 5)
  171.               + ((grayscaleImage[(yc+subLine)*WIDTH+xc+3] & 1) << 4)
  172.               + ((grayscaleImage[(yc+subLine)*WIDTH+xc+4] & 1) << 3)
  173.               + ((grayscaleImage[(yc+subLine)*WIDTH+xc+5] & 1) << 2)
  174.               + ((grayscaleImage[(yc+subLine)*WIDTH+xc+6] & 1) << 1)
  175.               + ((grayscaleImage[(yc+subLine)*WIDTH+xc+7] & 1) << 0);
  176.  
  177.         subLine++;
  178.        
  179.         if (subLine == 8)
  180.             {
  181.             subLine = 0;   
  182.             xc = xc + 8;
  183.             if (xc == WIDTH)
  184.                 {
  185.                 xc = 0;
  186.                 yc = yc + 8;   
  187.                 }
  188.             }
  189.         }
  190.     }
  191.  
  192. int compressC64Bitmap(char *targetName)
  193.     {
  194.     /*
  195.      * Some words about the proprietary .pk image format:
  196.      *
  197.      * First two bytes: The start address ($00 $04 -> $4000) of the file
  198.      *
  199.      * All other bytes contain the image data:
  200.      *
  201.      * - Any value that is NOT 123 is taken as it is
  202.      *   (1 Byte -> 8 Pixels, from left to right)
  203.      * - magic byte "123" (0x7B) + n -> repeat the previous byte n times
  204.      *                                  (n is never 0 or 1 here)
  205.      * - magic byte 123 + 0 -> byte 123
  206.      * - magic byte 123 + 1 -> end of file
  207.      *
  208.      */
  209.    
  210.     FILE *out;
  211.    
  212.     char compressedName[1000];
  213.     sprintf(compressedName,"%s.pk",targetName);
  214.     out = fopen(compressedName,"wb");
  215.     if (out == NULL)
  216.         {
  217.         printf("Failed to create '%s'\n",compressedName);
  218.         return -1;
  219.         }
  220.    
  221.     // Ensure that the file is going to be loaded at $4000
  222.     fputc(0x00,out); fputc(0x40,out);
  223.    
  224.     int lastCol = 0;
  225.    
  226.     int i = 0;
  227.    
  228.     while (i < (WIDTH*HEIGHT)/8)
  229.         {
  230.            
  231.         // in case the color value matches the "magic byte" (123) accidentally
  232.         if (c64bitmap[i] == 123)
  233.             {
  234.             fputc(123,out);
  235.             fputc(0,out);
  236.             lastCol = 123;
  237.             i++;
  238.             continue;
  239.             }
  240.  
  241.         // find sequence of repeating bytes
  242.         int sequenceCounter = 0;
  243.         while (c64bitmap[i+sequenceCounter] == lastCol && sequenceCounter < 255) sequenceCounter++;
  244.        
  245.         if (sequenceCounter >= 3)
  246.             {
  247.             fputc(123,out);
  248.             fputc(sequenceCounter-1,out);
  249.             i = i + sequenceCounter;
  250.             continue;
  251.             }
  252.            
  253.         if (sequenceCounter > 0)
  254.             {
  255.             for (int p = 0; p < sequenceCounter; p++) fputc(lastCol,out);
  256.             i = i + sequenceCounter;
  257.             continue;
  258.             }
  259.  
  260.         // Standard: normal byte
  261.         fputc(c64bitmap[i],out);
  262.         lastCol = c64bitmap[i];
  263.         i++;
  264.         }
  265.    
  266.     fputc(123,out); fputc(1,out); // write end-marker
  267.    
  268.     fclose(out);
  269.    
  270.     printf("Created %s\n",targetName);
  271.     return 0;
  272.     }
  273.  
  274. int createImageForValidation(char *targetName)
  275.     {
  276.     // Recreate PNG image from C64 bitmap
  277.     gdImagePtr im;
  278.     im = gdImageCreateTrueColor(WIDTH,HEIGHT);
  279.     int xc = 0;
  280.     int yc = 0;
  281.     int subLine = 0;
  282.     for (int i = 0; i < ((WIDTH*HEIGHT)/8); i++)
  283.         {          
  284.         int v; int c;
  285.  
  286.         int byte = c64bitmap[i];
  287.        
  288.         v = ((byte >> 7) & 1)*255; c = gdTrueColor(v,v,v); gdImageSetPixel(im, xc+0, yc+subLine, c);
  289.         v = ((byte >> 6) & 1)*255; c = gdTrueColor(v,v,v); gdImageSetPixel(im, xc+1, yc+subLine, c);
  290.         v = ((byte >> 5) & 1)*255; c = gdTrueColor(v,v,v); gdImageSetPixel(im, xc+2, yc+subLine, c);
  291.         v = ((byte >> 4) & 1)*255; c = gdTrueColor(v,v,v); gdImageSetPixel(im, xc+3, yc+subLine, c);
  292.         v = ((byte >> 3) & 1)*255; c = gdTrueColor(v,v,v); gdImageSetPixel(im, xc+4, yc+subLine, c);
  293.         v = ((byte >> 2) & 1)*255; c = gdTrueColor(v,v,v); gdImageSetPixel(im, xc+5, yc+subLine, c);
  294.         v = ((byte >> 1) & 1)*255; c = gdTrueColor(v,v,v); gdImageSetPixel(im, xc+6, yc+subLine, c);
  295.         v = ((byte >> 0) & 1)*255; c = gdTrueColor(v,v,v); gdImageSetPixel(im, xc+7, yc+subLine, c);
  296.  
  297.         subLine++;
  298.         if (subLine == 8)
  299.             {
  300.             subLine = 0;   
  301.             xc = xc + 8;
  302.             if (xc == WIDTH)
  303.                 {
  304.                 xc = 0;
  305.                 yc = yc + 8;   
  306.                 }
  307.             }
  308.        
  309.         }
  310.  
  311.     char extendedTargetName[500];
  312.     sprintf(extendedTargetName,"%s.png",targetName);
  313.  
  314.     FILE *out = fopen(extendedTargetName,"wb");
  315.     if (out == NULL)
  316.         {
  317.         printf("Failed to create %s\n",extendedTargetName);
  318.         gdImageDestroy(im);
  319.         return -1;
  320.         }
  321.     gdImagePng(im, out);
  322.     fclose(out);
  323.     gdImageDestroy(im);
  324.     return 0;
  325.     }
  326.  
  327. int processPic(char *sourceName,char *targetName)
  328.     {
  329.     if (importFileAsGrayscale(sourceName) != 0) return -1;
  330.     ditherGrayScaleIntoBlackAndWhite();
  331.     convertBlackWhiteImageIntoC64Bitmap();
  332.     if (compressC64Bitmap(targetName) != 0) return -1;
  333.     if (createImageForValidation(targetName) != 0) return -1;
  334.     return 0;
  335.     }
  336.  
  337. int processDirectory(char *sourcePath, char *targetPath)
  338.     {
  339.     DIR *directoryPointer = opendir(sourcePath);
  340.     struct dirent *files;
  341.     if (directoryPointer == NULL)
  342.         {
  343.         printf("Failed opening dir %s\n",sourcePath);
  344.         return -1;
  345.         }
  346.        
  347.     char newp[PATH_MAX];
  348.  
  349.     while ((files=readdir(directoryPointer))!=NULL)
  350.         {
  351.         if (!strcmp(files->d_name,".") || !strcmp(files->d_name,"..")) continue;
  352.    
  353.         if ((strlen(sourcePath)+strlen(files->d_name)+1) >= PATH_MAX)
  354.             {
  355.             printf("Won't read '%s/%s' as it exceeds the max. path length (%i characters).\n",sourcePath,files->d_name,PATH_MAX-1);
  356.             continue;
  357.             }
  358.        
  359.         sprintf(newp,"%s/%s",sourcePath,files->d_name);
  360.        
  361.         int c1 = files->d_name[strlen(files->d_name)-6]-'0';
  362.         int c2 = files->d_name[strlen(files->d_name)-5]-'0';
  363.         int slideNumber = (c1*10+c2);
  364.         if (slideNumber < 1) printf("Invalid slide name: %s (expected format: NN.png, e.g. 01.png)\n", files->d_name);
  365.         else
  366.             {
  367.             if ((strlen(sourcePath)+strlen(files->d_name)+1) >= PATH_MAX)
  368.                 {
  369.                 printf("Won't write '%s/%s' as it exceeds the max. path length (%i characters).\n",sourcePath,files->d_name,PATH_MAX-1);
  370.                 continue;
  371.                 }  
  372.            
  373.             char outPath[PATH_MAX];
  374.             sprintf(outPath,"%s/%.2i",targetPath,slideNumber);
  375.             processPic(newp,outPath);
  376.             }
  377.         }
  378.        
  379.     closedir(directoryPointer);
  380.     return 0;
  381.     }
  382.  
  383. int main(int argc, char *argv[])
  384.     {
  385.     if (argc == 3) return processDirectory(argv[1],argv[2]);
  386.  
  387.     printf("Usage: mkconvert <source directory> <target directory>\n");
  388.     printf("       e.g. mkconvert src out\n");
  389.     printf("         or mkconvert --help (for disclaimer, redistribution rules etc.)\n");
  390.     printf("\n");  
  391.    
  392.     if (argc == 2)
  393.         {
  394.         if (strcmp(argv[1],"--help") == 0 || strcmp(argv[1],"--about") == 0)
  395.             {
  396.             printf("About:\n");
  397.             printf("\"mkconvert\" was written 2012 by Magnus Kalkuhl <[email protected]>.\n");
  398.             printf("This program reads PNG images from \"src\" directory and converts them\n");
  399.             printf("into the proprietary .pk format that can then be read by the mkviewer on the C64.\n");
  400.             printf("\n");
  401.             printf("Important to know:\n");
  402.             printf("- All source files must be named 01.png, 02.png, 03.png etc.\n");
  403.             printf("- All source files must have a resolution of 320x200 pixel.\n");
  404.             printf("\n");
  405.             printf("Disclaimer:\n");
  406.             printf("I developed this program as a quick and dirty one-time-tool for a\n");
  407.             printf("presentation I did (see https://twitter.com/magnuskalkuhl/status/251697621931028480).\n");
  408.             printf("It is in no way an official software release and therefore comes without any guarantees.\n");
  409.             printf("Use at your own risk.\n");
  410.             printf("\n");
  411.             printf("Redistribution:\n");
  412.             printf("Feel free to redistribute this program or improve it\n");
  413.             printf("as long as the information about author, disclaimer and redistribution remains.\n");
  414.             printf("\n");
  415.             }
  416.         }
  417.    
  418.     return 0;
  419.     }
Advertisement
Add Comment
Please, Sign In to add comment