Advertisement
Guest User

mkconvert

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