{"id":12293,"date":"2011-07-12T07:20:08","date_gmt":"2011-07-12T12:20:08","guid":{"rendered":"http:\/\/www.shamusyoung.com\/twentysidedtale\/?p=12293"},"modified":"2011-07-12T08:52:27","modified_gmt":"2011-07-12T13:52:27","slug":"project-frontier-15-devil-of-a-problem","status":"publish","type":"post","link":"https:\/\/www.shamusyoung.com\/twentysidedtale\/?p=12293","title":{"rendered":"Project Frontier #15: Devil of a Problem"},"content":{"rendered":"<p>One of the things I like about this project is that it is uncluttered by goofy, awkwardly-designed libraries.  Sure, I could have grabbed some existing code for loading 3D files or animating figures, but I still would have been faced with the problem of getting them all to work together with my program. It&#8217;s possible that I might have saved time, but it&#8217;s also possible that I would have spent those four days pulling my hair out and solving strange dependency issues and going on fetch quests to get all the stuff it requires, only to discover that it didn&#8217;t work properly. Then I would have had the problem I feared: A huge, complex system that doesn&#8217;t work and no idea how to fix it.  <\/p>\n<p>And even if it did work, I wouldn&#8217;t really want to have an extra ten modules cluttering up my program, with thousands of lines of foreign (to me) code, the bulk of which I&#8217;ll never need or use, all in the name of saving me a day of work.  There&#8217;s a trade-off at work here (like there always is) where you exchange compactness, elegance, and maintainability for time.  At some point it becomes worth it, but I always advise caution when integrating foreign code.  Even if the other library was made by someone smarter and more experienced than myself, the truth remains that they didn&#8217;t design their code with my project in mind.  <\/p>\n<p>Worse, when integrating external libraries like this there&#8217;s always the chance you&#8217;ll spend the time and end up with nothing.  (Although, I think my professional experience has improved my ability to spot the packages likely to become duds and time-sinks.  In fact, <a href=\"?p=12271\">yesterday&#8217;s post<\/a> touched on that: Look at the interface of a library.  If it&#8217;s cluttered, obtuse, verbose, and confusing to use, you should be <strong>extremely<\/strong> reluctant to add it to your project, <em>even if it solves critical problems<\/em>.  Right now, you only have <em>one<\/em> problem.  Don&#8217;t trade it for six others.<\/p>\n<p>Hang on a second.  I feel like I&#8217;ve done this rant before&#8230;<\/p>\n<p><!--more-->(Minutes pass.)<\/p>\n<p>Crap.  Yes.  <a href=\"?p=9557\">I&#8217;ve already written a post on this very subject<\/a>.  Great.  Well, at least I can re-use <em>that<\/em>.  Here is a small excerpt:<\/p>\n<blockquote><p>It becomes a tremendous time-sink to try out a dozen different systems like this, because getting them to compile and integrating them with your project is such an enormous chore. I wouldn&#39;t mind doing the work if I knew it was what I wanted, but this is like assembling a car before you can test-drive it. It&#39;s entirely possible to blow a couple of hours on something and find out it&#39;s not at all what you need. And when you&#39;re working on it you have no idea how much more time it will take before you can see it work. You solve a problem to find another problem, on and on, never knowing if you&#39;re anywhere near the finish line.<\/p><\/blockquote>\n<p>Having said all that, the point stands that I can&#8217;t write every dang thing from scratch.  Sooner or later, I&#8217;ll need to break down and start using some existing code.  Actually, that time has come.<\/p>\n<p>I need to be able to load PNG files. Right now I&#8217;m using windows BMP files.  A lot of the textures I read in need to be masked, and BMP files don&#8217;t support any sort of transparency whatsoever.  (BMP is an awesome format.  No transparency. Horrible, almost non-existent compression. Platform-dependent. And the file format is still convoluted as hell.) Right now I have a hack in place where it will use hot pink (red=255, green=0, blue=255) as a mask.  For example, this is the texture I use for grass and flowers:<\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier15_1.jpg' class='insetimage'   alt='frontier15_1.jpg' title='frontier15_1.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>It works, but it doesn&#8217;t give me a way to have partial transparency.  I&#8217;m going to need that sooner or later, and the sooner I have it the less art assets I&#8217;ll have to convert later.<\/p>\n<p>I&#8217;ve dealt with PNG files in the past.  It seems that everyone&#8217;s go-to library for PNG files is a project called <a href=\"http:\/\/www.libpng.org\/pub\/png\/libpng.html\">libPNG<\/a>.  It suffers from a lot of problems I moan about in the post I linked above.  I really don&#8217;t want to untangle this particular string of Christmas lights and try to make it work in my project.  <\/p>\n<p>I try searching around, and I find <a href=\"http:\/\/openil.sourceforge.net\/\">this beauty<\/a>.  It&#8217;s a library called &#8220;devIL&#8221;, and it is a magnificent and well-built interface for reading images.  Let&#8217;s compare it to libPNG so I can give you another example of what I was talking about <a href=\"?p=12271\">yesterday<\/a>.  Let&#8217;s look at two programs, both of which are designed to read in a single PNG file and spew the color data to the console. <\/p>\n<p>libPNG is first:<\/p>\n<pre lang=\"c\" line=\"1\">\r\n#include <unistd .h>\r\n#include <stdlib .h>\r\n#include <stdio .h>\r\n#include <string .h>\r\n#include <stdarg .h>\r\n\r\n#define PNG_DEBUG 3\r\n#include <png .h>\r\n\r\nvoid abort_(const char * s, ...)\r\n{\r\n        va_list args;\r\n        va_start(args, s);\r\n        vfprintf(stderr, s, args);\r\n        fprintf(stderr, \"\\n\");\r\n        va_end(args);\r\n        abort();\r\n}\r\n\r\nint x, y;\r\n\r\nint width, height;\r\npng_byte color_type;\r\npng_byte bit_depth;\r\n\r\npng_structp png_ptr;\r\npng_infop info_ptr;\r\nint number_of_passes;\r\npng_bytep * row_pointers;\r\n\r\nvoid read_png_file(char* file_name)\r\n{\r\n        char header[8];    \/\/ 8 is the maximum size that can be checked\r\n\r\n        \/* open file and test for it being a png *\/\r\n        FILE *fp = fopen(file_name, \"rb\");\r\n        fread(header, 1, 8, fp);\r\n        if (png_sig_cmp(header, 0, 8))\r\n                abort_(\"[read_png_file] File %s is not recognized as a PNG file\", file_name);\r\n        \/* initialize stuff *\/\r\n        png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);\r\n        info_ptr = png_create_info_struct(png_ptr);\r\n        if (setjmp(png_jmpbuf(png_ptr)))\r\n                abort_(\"[read_png_file] Error during init_io\");\r\n        png_init_io(png_ptr, fp);\r\n        png_set_sig_bytes(png_ptr, 8);\r\n        png_read_info(png_ptr, info_ptr);\r\n\r\n        width = png_get_image_width(png_ptr, info_ptr);\r\n        height = png_get_image_height(png_ptr, info_ptr);\r\n        color_type = png_get_color_type(png_ptr, info_ptr);\r\n        bit_depth = png_get_bit_depth(png_ptr, info_ptr);\r\n\r\n        number_of_passes = png_set_interlace_handling(png_ptr);\r\n        png_read_update_info(png_ptr, info_ptr);\r\n\r\n        \/* read file *\/\r\n        if (setjmp(png_jmpbuf(png_ptr)))\r\n                abort_(\"[read_png_file] Error during read_image\");\r\n\r\n        row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);\r\n        for (y=0; y<height ; y++)\r\n                row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));\r\n\r\n        png_read_image(png_ptr, row_pointers);\r\n\r\n        fclose(fp);\r\n}\r\n\r\nvoid process_file(void)\r\n{\r\n        if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB)\r\n                abort_(\"[process_file] input file is PNG_COLOR_TYPE_RGB but must be PNG_COLOR_TYPE_RGBA \"\r\n                       \"(lacks the alpha channel)\");\r\n\r\n        if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_RGBA)\r\n                abort_(\"[process_file] color_type of input file must be PNG_COLOR_TYPE_RGBA (%d) (is %d)\",\r\n                       PNG_COLOR_TYPE_RGBA, png_get_color_type(png_ptr, info_ptr));\r\n\r\n        for (y=0; y<height; y++) {\r\n                png_byte* row = row_pointers[y];\r\n                for (x=0; x<width; x++) {\r\n                        png_byte* ptr = &#038;(row[x*4]);\r\n                        printf(\"Pixel at position [ %d - %d ] has RGBA values: %d - %d - %d - %d\\n\",\r\n                               x, y, ptr[0], ptr[1], ptr[2], ptr[3]);\r\n\r\n                        \/* set red value to 0 and green value to the blue one *\/\r\n                        ptr[0] = 0;\r\n                        ptr[1] = ptr[2];\r\n                }\r\n        }\r\n}\r\n\r\nint main(int argc, char **argv)\r\n{\r\n        if (argc != 3)\r\n                abort_(\"Usage: program_name <file_in> <file_out>\");\r\n\r\n        read_png_file(argv[1]);\r\n        process_file();\r\n        return 0;\r\n}\r\n<\/file_out><\/height><\/png><\/stdarg><\/string><\/stdio><\/stdlib><\/unistd><\/pre>\n<p>(NOTE: The very last line is not part of the program, but part of IDIOTIC WordPress markup trying to do my thinking for me.  Even with the help of a plugin, I can&#8217;t get it to stop treating my C++ like HTML.  (And if I use &amp;gt; and &amp;lt;, then it shows those literally in the code, which is wrong and stupid.  I looked, and I can&#8217;t get WP to stop doing this very annoying thing. Arg. But this is another rant for another time.))<\/p>\n<p>Now here is a functionally identical program using devIL:<\/p>\n<pre lang=\"c\" line=\"1\">\r\n#include <stdio .h>\r\n#include <stdlib .h>\r\n\r\n#include \"il.h\"\r\n\r\nint main()\r\n{\r\n\r\n  ilInit();\r\n  \/\/ Loading an image\r\n  ILboolean result = ilLoadImage( \"4px.png\" ) ;\r\n  int size = ilGetInteger (IL_IMAGE_SIZE_OF_DATA) ;\r\n  printf(\"Data size:  %d\\n\", size );\r\n  ILubyte * bytes = ilGetData() ;\r\n  \/\/print out the byte data\r\n  for( int i = 0 ; i < size; i++ )  {\r\n    printf( \"%d\\n\", bytes[ i ] );\r\n  }\r\n}<\/pre>\n<p><\/stdlib><\/stdio><\/p>\n<p>(Note that I removed the error-checking from both programs, which only served to make the difference between the two more extreme.)<\/p>\n<p>I don't know why libPNG has you do the manual parsing of the file like that. Perhaps there's some situation where I might want to only read part of the file? Or just read the header? I don't know.  But 99.9% of the people who try to use libPNG just want the raw image data, and don't care to see the guts of the PNG file format like this. <\/p>\n<p>Note that devIL (\"Developers Image Library\") was once called \"OpenIL\", but the name was changed at the request of <a href=\"http:\/\/en.wikipedia.org\/wiki\/Silicon_Graphics\">SGI<\/a>, the folks who maintain OpenGL.  Even so, I now keep devIL in my box of indispensable tools, along side OpenGL (Graphics Library) and OpenAL (Audio Library) as silver-bullet problem solver. The interface is focused and reasonable, the function naming is logical, and it does exactly what I need.  It's actually a wrapper for libPNG, hiding all of that manual file parsing and reducing the reading of images to a couple of lines.  <\/p>\n<p>This is actually something that's been needed for a long time. While searching for a solution to this problem I encountered many forums where some starry-eyed would-be game developer (not that I have any room to talk) would ask how to load a PNG file, only to be sent to their doom.  They were expecting a line or two of code, maybe a hidden function somewhere in OpenGL?  Of course, OpenGL's focus is such that it wouldn't make any sense to have an image loader, but I can understand why a newcomer would expect there to be such a thing.  And I can certainly understand why they would be baffled at why they need to find and integrate a library with multiple dependencies, and then add 100 lines of code to their program, just to load a simple PNG image.  <\/p>\n<p>So congratulations to devIL creator Denton Woods:  This was a much needed library, and well-executed.<\/p>\n<p>(I don't know WHAT this says about the PNG image file format.  In all these years, nobody has written a simplified alternative to libPNG? This must be one hell of a convoluted format.)<\/p>\n<p>There's not much to show in the way of screenshots, though.  I mean, the whole point was to move from BMP to PNG files, and that's it.  Everything is supposed to look exactly the same. <\/p>\n<p>Ah! There is one new thing.  Now I can make the terrain patches partly transparent. <\/p>\n<p><table   class=\"\" cellpadding='0' cellspacing='0' border='0' align='center'><tr><td><img src='https:\/\/www.shamusyoung.com\/twentysidedtale\/images\/frontier15_2.jpg' class='insetimage'   alt='frontier15_2.jpg' title='frontier15_2.jpg'\/><\/td><\/tr><\/table><\/p>\n<p>This is a spot where it's transitioning from sand to dirt.  Previously the spots had hard edges, making it look like a cow pattern.  The soft edges make it a little more organic.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>One of the things I like about this project is that it is uncluttered by goofy, awkwardly-designed libraries. Sure, I could have grabbed some existing code for loading 3D files or animating figures, but I still would have been faced with the problem of getting them all to work together with my program. It&#8217;s possible [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[66],"tags":[],"class_list":["post-12293","post","type-post","status-publish","format-standard","hentry","category-programming"],"_links":{"self":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/12293","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=12293"}],"version-history":[{"count":0,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=\/wp\/v2\/posts\/12293\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=12293"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=12293"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.shamusyoung.com\/twentysidedtale\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=12293"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}