SHARE
TWEET

mkconvert

a guest Nov 5th, 2012 62 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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.         }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top