Guest User

mkconvert

a guest
Nov 5th, 2012
147
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

Adblocker detected! Please consider disabling it...

We've detected AdBlock Plus or some other adblocking software preventing Pastebin.com from fully loading.

We don't have any obnoxious sound, or popup ads, we actively block these annoying types of ads!

Please add Pastebin.com to your ad blocker whitelist or disable your adblocking software.

×