Project Frontier #15: Devil of a Problem

 By Shamus Jul 12, 2011 120 comments

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’s possible that I might have saved time, but it’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’t work properly. Then I would have had the problem I feared: A huge, complex system that doesn’t work and no idea how to fix it.

And even if it did work, I wouldn’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’ll never need or use, all in the name of saving me a day of work. There’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’t design their code with my project in mind.

Worse, when integrating external libraries like this there’s always the chance you’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, yesterday’s post touched on that: Look at the interface of a library. If it’s cluttered, obtuse, verbose, and confusing to use, you should be extremely reluctant to add it to your project, even if it solves critical problems. Right now, you only have one problem. Don’t trade it for six others.

Hang on a second. I feel like I’ve done this rant before…

(Minutes pass.)

Crap. Yes. I’ve already written a post on this very subject. Great. Well, at least I can re-use that. Here is a small excerpt:

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’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’s entirely possible to blow a couple of hours on something and find out it’s not at all what you need. And when you’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’re anywhere near the finish line.

Having said all that, the point stands that I can’t write every dang thing from scratch. Sooner or later, I’ll need to break down and start using some existing code. Actually, that time has come.

I need to be able to load PNG files. Right now I’m using windows BMP files. A lot of the textures I read in need to be masked, and BMP files don’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:

frontier15_1.jpg

It works, but it doesn’t give me a way to have partial transparency. I’m going to need that sooner or later, and the sooner I have it the less art assets I’ll have to convert later.

I’ve dealt with PNG files in the past. It seems that everyone’s go-to library for PNG files is a project called libPNG. It suffers from a lot of problems I moan about in the post I linked above. I really don’t want to untangle this particular string of Christmas lights and try to make it work in my project.

I try searching around, and I find this beauty. It’s a library called “devIL”, and it is a magnificent and well-built interface for reading images. Let’s compare it to libPNG so I can give you another example of what I was talking about yesterday. Let’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.

libPNG is first:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include <unistd .h>
#include <stdlib .h>
#include <stdio .h>
#include <string .h>
#include <stdarg .h>
 
#define PNG_DEBUG 3
#include <png .h>
 
void abort_(const char * s, ...)
{
        va_list args;
        va_start(args, s);
        vfprintf(stderr, s, args);
        fprintf(stderr, "\n");
        va_end(args);
        abort();
}
 
int x, y;
 
int width, height;
png_byte color_type;
png_byte bit_depth;
 
png_structp png_ptr;
png_infop info_ptr;
int number_of_passes;
png_bytep * row_pointers;
 
void read_png_file(char* file_name)
{
        char header[8];    // 8 is the maximum size that can be checked
 
        /* open file and test for it being a png */
        FILE *fp = fopen(file_name, "rb");
        fread(header, 1, 8, fp);
        if (png_sig_cmp(header, 0, 8))
                abort_("[read_png_file] File %s is not recognized as a PNG file", file_name);
        /* initialize stuff */
        png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
        info_ptr = png_create_info_struct(png_ptr);
        if (setjmp(png_jmpbuf(png_ptr)))
                abort_("[read_png_file] Error during init_io");
        png_init_io(png_ptr, fp);
        png_set_sig_bytes(png_ptr, 8);
        png_read_info(png_ptr, info_ptr);
 
        width = png_get_image_width(png_ptr, info_ptr);
        height = png_get_image_height(png_ptr, info_ptr);
        color_type = png_get_color_type(png_ptr, info_ptr);
        bit_depth = png_get_bit_depth(png_ptr, info_ptr);
 
        number_of_passes = png_set_interlace_handling(png_ptr);
        png_read_update_info(png_ptr, info_ptr);
 
        /* read file */
        if (setjmp(png_jmpbuf(png_ptr)))
                abort_("[read_png_file] Error during read_image");
 
        row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * height);
        for (y=0; y<height ; y++)
                row_pointers[y] = (png_byte*) malloc(png_get_rowbytes(png_ptr,info_ptr));
 
        png_read_image(png_ptr, row_pointers);
 
        fclose(fp);
}
 
void process_file(void)
{
        if (png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_RGB)
                abort_("[process_file] input file is PNG_COLOR_TYPE_RGB but must be PNG_COLOR_TYPE_RGBA "
                       "(lacks the alpha channel)");
 
        if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_RGBA)
                abort_("[process_file] color_type of input file must be PNG_COLOR_TYPE_RGBA (%d) (is %d)",
                       PNG_COLOR_TYPE_RGBA, png_get_color_type(png_ptr, info_ptr));
 
        for (y=0; y<height; y++) {
                png_byte* row = row_pointers[y];
                for (x=0; x<width; x++) {
                        png_byte* ptr = &(row[x*4]);
                        printf("Pixel at position [ %d - %d ] has RGBA values: %d - %d - %d - %d\n",
                               x, y, ptr[0], ptr[1], ptr[2], ptr[3]);
 
                        /* set red value to 0 and green value to the blue one */
                        ptr[0] = 0;
                        ptr[1] = ptr[2];
                }
        }
}
 
int main(int argc, char **argv)
{
        if (argc != 3)
                abort_("Usage: program_name <file_in> <file_out>");
 
        read_png_file(argv[1]);
        process_file();
        return 0;
}
</file_out></height></png></stdarg></string></stdio></stdlib></unistd>

(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’t get it to stop treating my C++ like HTML. (And if I use &gt; and &lt;, then it shows those literally in the code, which is wrong and stupid. I looked, and I can’t get WP to stop doing this very annoying thing. Arg. But this is another rant for another time.))

Now here is a functionally identical program using devIL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <stdio .h>
#include <stdlib .h>
 
#include "il.h"
 
int main()
{
 
  ilInit();
  // Loading an image
  ILboolean result = ilLoadImage( "4px.png" ) ;
  int size = ilGetInteger (IL_IMAGE_SIZE_OF_DATA) ;
  printf("Data size:  %d\n", size );
  ILubyte * bytes = ilGetData() ;
  //print out the byte data
  for( int i = 0 ; i < size; i++ )  {
    printf( "%d\n", bytes[ i ] );
  }
}

(Note that I removed the error-checking from both programs, which only served to make the difference between the two more extreme.)

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.

Note that devIL ("Developers Image Library") was once called "OpenIL", but the name was changed at the request of SGI, 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.

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.

So congratulations to devIL creator Denton Woods: This was a much needed library, and well-executed.

(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.)

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.

Ah! There is one new thing. Now I can make the terrain patches partly transparent.

frontier15_2.jpg

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.

A Hundred!20There are 120 comments here. I really hope you like reading.


  1. Alan says:

    Back when I started doing website work, while PNG files were good for some things, several browsers at that time didn’t render the transparent images, meaning for transparency I still had to use .gif images, and their 256 colour glory.

    Glad things are a little more up to date in your world.

    • This is still the case with IE6. If you want your site to work properly in IE6 and you’re using transparent PNGs, you need to use an ugly proprietary hack from Microsoft on each image.

      You can apply this (non-standard) CSS class to images:
      filter: progid:DXImageTransform.Microsoft.AlphaImageLoader(...);

      But there are still issues with it. Luckily there are some nice JavaScript libraries to automate this process and fix some of the problems.

      This article discusses it in depth.

      • Kerin says:

        if you want your site to work properly in IE6, you have bigger problems.

        • HeroOfHyla says:

          Half of the computers at my high school were still using XP and IE6 when I finished my last semester there in 2010. The other half were using Windows 2000.

          The software dev teacher actually had a big bunch of newer computers delivered for his class, but he couldn’t get them to work on the network properly. They were delivered in my sophmore year and were still sitting in storage when I graduated.

          • Shamus says:

            And then someone complained that the school needed to build an expansion because they didn’t have enough storage space.

            And someone else complained we weren’t spending enough on education.

            Sigh.

            • Lalaland says:

              Public procurement is a tragi-comedy at the best of times. The end of year spending sprees on crap that no-one needs; driven by the fact that any ‘savings’ are returned to centre and come off the top of next years budget. The vague tender documents that violate the laws of physics, e.g. ‘I want a sub 2.0kg notebook with a 17″ screen, oh, and wings’. The complete inability to transact business over the summer months as conflicting holiday schedules make it impossible to get everyone necessary to a decision together at once.

              The problem with insufficient IT resourcing meaning schools waste their IT budgets is universal. Here in Ireland most schools get a pittance for their IT budget and no assistance in spending it wisely. More often than not they turn to a parent or the local IT shop and get crappy kit at terrible prices because at least it comes with a ‘guru’ to make it all work. I’ve said it to senior people until I’m blue in the face ‘Volume = Discount’ but just so everyone can feel ‘independent’ and ‘in control’ we divvy the pot up until it’s too small to attract proper discounts. As a result schools buy the cheapest kit in capital terms that costs more than twice as much to run (in my company cheap = 70W an hour, business standard = 32W an hour).

        • Lalaland says:

          Yes like grumpy corporate clients that won’t let it die. I see this a lot in my job (pre-sales tech at a major OEM) where customers tell me ‘I love Win 7 but I need IE 6′. A lot of early web portals, web delivered apps (CRM & ERP suites especially) and custom coded stuff were designed specifically for the ugliness that is IE6′s web engine. The cost of updating those tools and paying for the requisite engineering time is a lot higher than making your vendors jump through hoops to accommodate your crappy IT infrastructure. XP isn’t dead, it’s undead as a ‘downgrade’ used in public sector and large corporate IT

          Don’t even start me on the necessity of particular Java or JInitiator (ugh) revisions for the same reasons, ‘Write once, run anywhere’ my behind.

          • Winter says:

            ‘I love Win 7 but I need IE 6′

            AAAaaaaaaaaaaaaaaaaaaaaaAAAAaaaaaaaaaaaaaa

            It’s madness! It’s madness!!

          • Simon Buchan says:

            Windows 7 integrates Microsoft’s Virtual PC, so you can download a special Windows XP image here and get IE 6 running in Windows 7, sorta. It’s actually running inside Windows XP running inside 7, so you get the Luna blue plastic window frame instead of the shiny Aero frame, and some smaller weirdness due to it essentially running under a different user and filesystem. It’s great for debugging!

            • Lalaland says:

              You’re right but it’s a full O/S image so it requires all the things that a normal O/S does, GPO’s, anti-virus, malware protection and patching. This means that for anything beyond debug it’s a complete pain as effectively the IT team is left managing two environments.

              There are some more sophisticated solutions that virtualise it on the back end and present it as an icon on the desktop but that costs money and if you’re doing that why not redo the web app in question. It’s a complete pig of a situation but as long as that XP support clock keeps running it won’t go away.

              • Simon Buchan says:

                Not sure what you mean by virtualization. It does show VPC applications beside regular apps, it’s possible to use them without knowing it.

                I’d guess that they simply can’t upgrade applications – built by contractors who have gone out of business, source code lost, or simply can’t budget time for it. Virtualizing is quicker and fiscally safer, in a lot of cases. Not that I *like* that, of course!

        • Factoid says:

          I get where you’re coming from, but IE6 is still a measurable percentage of internet browsers in use. around 5% if I remember correctly.

          If you’re working on some kind of ecommerce website, your bosses will not take kindly to the sentiment of “to hell with 5% of our potential paying customers having a satisfactory viewing experience”

          • aldowyn says:

            why are 5% of people still using IE6 when it’s all the way to IE8 and IE sucks anyway? *shrug*

            • Felblood says:

              Irrelevant.

              They have money, and you want their money, and they won’t pay you to preach the virtues of Firefox at them.

            • Jabrwock says:

              Institutional inertia among other things.

              If a business has 900 employees and an IT staff of 1-2, they likely have a bunch of web apps that HR depends on that require IE 6 or they break.

            • peter says:

              IE9, actually.
              and the later versions aren’t half bad.
              still, chrome is faster, opera has more options while being marginally faster, firefox can be persuaded to just about anything with addons. safari is… safari, there’s not that great a reason to pick internet explorer.
              especially (in europe at least) since microsoft is forced to send a windows update that allows you to choose a browser.

              aldowyn, as said before, backwards compatibility with some older business oriented sites. thankfully they’re decreasing, but there’s still a sizable minority. perceived stability is also a point, upgrading is seen as bad by certain companies.

            • Zak McKracken says:

              “IE sucks anyway”
              Is a long-standing truth, yet most people have never heard about it. In most people’s mind:
              - Every Computer needs “a Windows” to run
              - Internet == Internet Explorer
              - Word processing == Word
              Everything else is some newfangled experimental stuff that you probably need a diploma for. It was hard enough to understand how to use a computer at all, why do you want to make it even more complicated?

              … I’m not trying to make fun of these people, because I can pretty much understand how you can know almost nothing about a topic you’re not interested in and have no connection to. The truth is that most of the people stuff on the internet is made for are no longer assorted geeks but regular “I bought my car because I liked the colour” people.

              • Lalaland says:

                +1 The cost of change in retraining is a lot higher when the persons primary job task isn’t using the computer. Particularly in highly unionised workplaces where even changing O/S can trigger retraining or payments (I’ve had issues that could be fixed by an O/S upgrade but that’s off the table due to union rules).

            • HeroOfHyla says:

              Because my parents run XP with 768 MB of RAM and don’t want to learn anything new.

    • Mephane says:

      I remember this bug. When I first encountered it (posting a PNG image I had done for a forum), I said “screw IE” and finally switched to Mozilla. Never went back, heh. Imho in terms of features PNG is the ultimate image format for both the web and a lot of other use cases. Still very small in file size, no loss of quality, alpha channel transparency. :)

  2. Ingvar says:

    From what I understand, writing PNG is pretty easy. However, there’s, like, a bazillion different subformats, with various things, whistles, bells and (mis)features.

    You can have a progressive image, where you have pixels not laid out in a nice “starts with top left pixel, going right pixel by pixel in a line, then down one line” (conceptually, at least, I think the image data is then compressed with zlib compression) but rather ‘start with pixel 0,0, then 0,16 , then 16,0, then 16,16, then…’ and gradually shifting this mask around so as to allow a loader (presumably from a slow data source) to show better and better approximations of the real image.

    And, I mean, yes, I can see that being handy in a few (and I do mean “few”) cases. But if you’re saying ‘I support loading PNG images from a file’ you better deliver on it. Saying ‘you can write PNG images’ is a much easier problem, since it doesn’t matter if you can save in one or all formats, it’s still a valid PNG image, right?

    I did look into augmenting a library I use for writing PNGs to support loading, started reading up on the spec and, well, recoiled in horror. And I write assemblers for fun.

    • You’re referring to interlacing. It was designed back in the dial-up days as a way to deliver images by slowly bringing them into focus. You load a blurry image quickly, then a less blurry image, then less blurry; again and again until you have the clear image.

      It’s generally agreed amongst web developers that this is unnecessary and not a good solution even for slow connections. So it’s rarely ever used, but still needs to be supported.

      • Ingvar says:

        I am, yes, although there’s line-by-line interlacing as well as more spread interlacing and I would not be surprised if PNG supports both.

      • Bodyless says:

        This Interlacing is not only used for web browsing. Just look at any Unreal 3 enigne game like Mass Effect. You will often see blurry textures after a loading screen. Why? They only loaded a small part of the texture file to cut down loading times and then load the rest of the file while the game is already running.

        • Abnaxis says:

          I don’t know if that’s really interlacing. I always assumed that the engine wasn’t interlacing, but rather loading the low-LoD textures (i.e. the textures normally reserved for far away object) and replacing them with the larger textures as they loaded.

          Of course, the low detail textures may very well be constructed as inerlaced images…

        • Garden Ninja says:

          Interesting. I always assumed that the effect you’re describing was do to coloring the polys themselves before the texture was loaded, rather than a lowres texture then the high res one.

        • utzelgrutzel says:

          …and if you are waiting in the loading screen for 2 minutes longer than necessary for your own PC, because the host is a slow laptop, it will just do nothing so you can see the low resolution textures in full glory like everyone else.

    • Simon Buchan says:

      PNG isn’t actually too bad a spec, you can implement a specific profile with (im guessing) ~200 lines of code (including a split out subset of zlib, ~250).

      You have a file signature, then a tag stream, there’s a bunch of mostly infomational tags, the important ones are IHDR (image header) tag, then IDAT (image data), then IEND (in that order, obviously).

      IHDR has a bunch of fields saying what type of image this is: width, height, bits per channel, number and type of channels (gray, color, palleted, alpha), and if it’s interlaced or progressive.

      IDAT stores a zlib compressed block of rows from top to bottom, each preceded by a byte saying which one of 5 (simple) filters to apply to get better results from zlib. Interlaced is just this repeated 7? times at different grid offsets and sizes.

      The complexity of *really* handling PNG is in progressive display, color correction, editing it without losing informational tags, etc…

  3. Kdansky says:

    If you ever want to parse XML*, do yourself a favour and avoid TinyXML (or tcipp) like the plague. It also offers convoluted controls, doesn’t care much about CString and is generally a pain with Unicode. Use pugiXML instead. Correct DOM support, flawless Unicode integration.

    *I know people rant about how XML is inefficient (and slow to parse) and all, but damn, it’s such a practical format to store game data. Human readable, hierarchic, and writing DOM code is a bit wordy, but very simple and easy to understand.

  4. skeeto says:

    If you think libpng is bad, don’t look at libvorbis!

    Java has always had a reasonable interface for reading in images,


    File pngfile = new File("example.png");
    try {
    Image img = ImageIO.read(pngfile);
    // Use it
    } catch (java.io.IOException e) {
    LOG.warning("failed to open '" + pngfile + "': " + e);
    }

  5. Zukhramm says:

    red=255, blue=0, green=255

    That’s yellow, I think you mean blue=255 instead.

  6. Glenn says:

    Ahh, the lovely trials of external file loading. Seriously beyond the first time trying to render a 3d object, this is one of the most obtuse programming things wherein the internet seems helpful, but is so, so not.

  7. kikito says:

    I’m pretty sure that the name of that “Hot Pink” color is “Fuchsia”. At least according to the CSS standard.

    Wikipedia even mentions that “It is also commonly used to indicate transparency”.

  8. ClearWater says:

    I wonder why your #include tags are not “properly” closed in the devIL example.

  9. X2-Eliah says:

    On that texture pic – looking better. transparency does add a bit of a consistency (also shadows).. Nice.

    Also, does this mean we will finally see pink-coloured objects in your world? squeeee

  10. I certainly see your argument with confusing and needlessly long library interfaces, but personally I think the greater sin is lack of understandable documentation.
    I personally like to solve the libPNG problem by having a separate class or set of classes (maybe a static Utility class or similar) and encapsulating the complexity in that class. It is slightly more work to copy paste their example code, but as long as the calling code does its work in a line or two, it doesn’t matter to me.
    I find it much worse if they code a short example showing the basic usage that most people go for (such as loading a PNG) but there’s no documentation for other features of the library.
    I’m not sure if this is particularly a sin of libPNG, but I’ve certainly found it problematic with scripting languages. I’ve recently been integrating a scripting language into my application so I can have scripts define the rules for procedurally generating content. I started with LUA, which was really quick and easy to set up, and the basic parameter passing between LUA and C++ was really simple. The problem came when trying to pass Arrays back and forth, there was barely any documentation for it, and trying to work out multidimensional arrays was near impossible.
    So I’ve moved to Python instead, which took longer than LUA to set up, as its tutorial for embedding Python as a scripting language only covers the basics, and the C API documentation is severely lacking. Now I’ve got it working, it’s much better than LUA, but I think the biggest sin of free libraries is their lack of documentation and tutorials, rather than their verbose functions.

  11. DrMcCoy says:

    In all these years, nobody has written a simplified alternative to libPNG?

    Eh, I guess it’s just that you do it once, put it in a wrapper function and then never look at it again. You’ve got a headscratchy moment for 10 minutes and then you can simply ignore it.

  12. Dev Null says:

    In all these years, nobody has written a simplified alternative to libPNG? This must be one hell of a convoluted format.)

    And now that you’ve finally found an “alternative” you say its really just a wrapper, and you have to have libPNG and all its dependencies too? Sheesh; that really _must_ be one hellova messy format…

    • DrMcCoy says:

      Well, the only dependency libpng has is zlib (which itself is self-contained). Not that much of a problem, IMHO. And I for one often wind up with needing zlib anyway, independantly.

      But yeah, DevIL depends on libjpeg, libmng, libtiff, libxpm, liblcms, SDL, the X and the OpenGL libraries in addition to libpng. That’s the standard Debian package, at least; if you’re compiling it yourself, it can probably be configured to drop a lot.

      (I guess I’m not the target audience for DevIL anyway. I don’t think I ever need half of the formats supported by it and I’ve got no problems with using libpng, libjpeg and libgif directly. And several of the other formats are easy enough to write readers for myself (BMP, ICO, TGA, TIFF, PPM, DDS) or I need to do that anyway to accomodate for hacks like unofficial compression algorithms (or, some sort of hacky transparency in BMP).)

  13. sab says:

    Now I don’t want to be a naggy nag, but wouldn’t that bmp texture screenshot look better if it were a png or gif? The jpeg-artefacts kinda give it that semi-transparent look now.

  14. Abnaxis says:

    If you took all the error handling out, what are all those _abort() calls?

    • Shamus says:

      Those are tied to actual processing, so I had to leave them in or re-write them.

      Here’s the original, for comparison:

      http://zarb.org/~gc/html/libpng.html

      • Abnaxis says:

        Ah, I see. You took out all the explicit “this statement is just here for error checking” but stopped short of taking out ALL the error checking at the point where you would have had to remove the if() statment wrapping half the function calls.

        Through all the extraneous testing I gotta ask, why can’t they just throw errors instead of going through all this if(error) crap? Just so inexperienced coders can be confused when they get odd null pointer exceptions?

        • anna says:

          Because it’s a C library, and C doesn’t have built-in exception handling. Well, beyond signals, and those are OS-dependent and not really designed for errors that originate in userspace anyway.

          Also, Shamus, thank you thank you thank you for showing me DevIL. Now I can rip out 150 lines of poorly understood (by me) libpng-related code and replace it with something that doesn’t make me want to find the libpng developers and harm them.

        • Wtrmute says:

          libPNG is plain C, so no throwing allowed. Instead, you can use setjmp()/longjmp() to simulate throw/catch, which is what libPNG seems to be actually doing here…

          Personally, I would go for NULL/zero results in function returns and some sort of png_get_last_error() function if I were the one designing that interface. Having to use it as it is, I’d probably just hide the setjmp call under a more descriptive macro, like TRY_EXCEPT(PNG_PTR, …) or RAISE(PNG_PTR, …) (as in Alef‘s usage).

  15. Sean Hagen says:

    I think whatever you use to put code into your page is confusing C/C++ with HTML. At the bottom of the libPNG example is this gem:

    </file_out></height></png></stdarg></string></stdio></stdlib></unistd>

    Never mind, I’m an idiot.

  16. Pyroka says:

    If I recall correctly, libPNG is written by the folks that created the PNG file format, and as such, it does everything that a png file can possibly do. Yes it is a horrendous library to implement (but no worse than libJpeg, libGif, etc.) However, it does have an advantage over DevIL, in that DevIL is build with OpenGL solely in mind (loading an image gives you an ILuint handle)which is perfect for you, being that your app is nicely OpenGL, but for cross platform stuff… not so much. (I also seem to recall that DevIL will essentially create two copies of your image, one OpenGL one and one for itself, it also seems to duplicate some of the efforts of OpenGL) I may be out of date on those last points though, it’s been a while since I looked at it.

    (Having said that if you’re writing cross platform/renderer code you’re a masochist and deserver everything you get)

    P.S. So glad you’ve finally moved on from PNGs, now we’ll wait till unpacking the PNGs takes so long that you add a compile step to convert from PNG into a different format :p

  17. Mephane says:

    Aaaaaah the pain, make it stop. (Looking at the first code example)

    Whoever thought such would be a sufficient interface for a library used by anyone must be a madman, masochist, sadist or any combination of those. No one in their right mind would design an interface like this even if it were code just to be used by themselves.

    On the other hand, while I like the cleanness of the second code example, it surely looks like it’s operating on some hidden global state. It sure seems like a fine library, but still is one of the many cases where I come to think, did anyone ever think about why C++ has been invented? A lot of these libraries suffer, even if done well, from old C paradigms modifying hidden global state, requiring you to use free-floating functions (sometimes spread over several headers) on some pointer etc. in order to achiebe anything, instead of something like

    PngImage image = PngImage::LoadFromFile("mypicture.png");
    size_t width = image.Width(); // width in pixels
    size_t height = image.Height(); // height in pixels
    size_t size = image.Size(); // size in bytes
    size_t imageSize = image.Size();

    P.S.: I have a gut feeling that many people are already so used to awkward libraries and interfaces that the most they ever do is write a wrapper in order to be able to use it for themselves, and then decide to release that wrapper to the public.

    • DrMcCoy says:

      Said hidden global state will also be a PITA if you’re loading images from more than one thread. DevIL 2 will get a thread-safe API, though, if you can believe the news blurbs on its website.

    • Jordi says:

      This is pretty much exactly what I thought. This is also how higher level languages seem to tend to do it. I’m working in C# now and you can just do ‘new Bitmap(“filename.png”)’, or .bmp, .jpg, .gif, etc. I’m sure it’s a lot slower though. To be honest, since I’m used to this high level stuff, when I started reading this article I was pretty surprised that this was apparently an issue.

    • Pyroka says:

      Actually most middleware like this is written in C so you can use it from pretty much any language known to man, ugly yes and LibPNG is a particularly bad example, but there is some method behind the madness

  18. bit says:

    Actually, the last image is looking noticeably better, if purely because it is rid of all that horrid anti-aliasing.

  19. Kell says:

    Shamus, just curious: if grayscale transparency is what you want ( and it’s nice ) why not use .tga? In all my fps engine experience, textures are tga for that, not png. I’m sure there’s a good reason, I just wonder what it is.

    • Shamus says:

      If I had found a .tga importer first, I would have used that instead.

      • DrMcCoy says:

        Well, there’s libtga. It’s also simpler to use than libpng.

        The TGA format is actually quite simple too. If you ignore things like 8bpp paletted images and RLE compression, a reader is written pretty easily.

        • Zak McKracken says:

          That’s interesting. I thought TGA had died a silent death more than ten years ago, at the hands of TIFF … But then I haven’t seen a BMP in a long time, either, and you’re using those, too.
          I guess good formats from a user perspective (jpg, tif, png, full stop) are not the same as good formats from a programmer’s perspective …

          Crazy idea: has anyone ever tried to create textures out of vector images, like eps or svg? Infinite texture resolution! Probably also infinite pain to implement :o)

  20. Bryan says:

    Uh. No love for SDL_image?

    That’s a pity, since you’re already using SDL. :-) (Though getting a surface is a step or two away from getting a usable GL texture, so maybe not.) It *looks* like its image loading code is even self-contained except for PNG, JPEG, and TIFF, which only require the dependent libraries at runtime (not development time).

    It also loads TGA, so for everyone above suggesting that… :-)

    I should also note that there are a *ton* of other related SDL_* libraries, for lots and lots of values of *. SDL_net (networking), SDL_sound (sound file decoding), SDL_ttf (font handling), SDL_mixer (sound mixing), and SDL_gfx (bitmap drawing, rotating, zooming, etc., IIRC) are the ones I have installed right now. (That’s in addition to SDL_image.)

    See a somewhat-full list (or at least, what libsdl.org knows about) here. (Skip the first three or four pages though. Start with the libraries whose names actually start with “SDL”. And be aware what the status is on various OSes for the libraries you’re looking at…)

  21. Simon Buchan says:

    Amusingly, Direct3D comes with a support library that will handle the majority of image formats completely painlessly: http://msdn.microsoft.com/en-us/library/bb172801.aspx

  22. Andy says:

    I always end up recommeneding LodePNG if you need to load AND save PNG files, otherwise the same author makes PicoPNG which just handles loading and is a single header file.

    LibPNG is faster at saving files it seems but is it worth the pain of using it? Never has been for me.

    • Mystran says:

      PicoPNG is my goto loader as well; single .cpp file, single function to call. I’ve used libpng years ago, and it wasn’t THAT bad (and the streaming capability is kinda cool for network loading) but 99% picoPNG does everything I want with the absolute minimum of pain. :)

  23. [...] on Shamus Young’s blog, he recently said this when talking about a programming project of his: One of the things I like about this project is that it is uncluttered by goofy, awkwardly-designed [...]

  24. Why not use TARGA?
    It supports alphas, has simple RLE compression and is very easy to implement a reader and writer for yourself. Best for last; the header contain offset information so you can store where in each image the centre is (great for sprite explosiones and whatnot.

One Trackback

  1. [...] on Shamus Young’s blog, he recently said this when talking about a programming project of his: One of the things I like about this project is that it is uncluttered by goofy, awkwardly-designed [...]

Leave a Reply

Comments are moderated and may not be posted immediately. Required fields are marked *

*
*

Thanks for joining the discussion. Be nice, don't post angry, and enjoy yourself. This is supposed to be fun.

You can enclose spoilers in <strike> tags like so:
<strike>Darth Vader is Luke's father!</strike>

You can make things italics like this:
Can you imagine having Darth Vader as your <i>father</i>?

You can make things bold like this:
I'm <b>very</b> glad Darth Vader isn't my father.

You can make links like this:
I'm reading about <a href="http://en.wikipedia.org/wiki/Darth_Vader">Darth Vader</a> on Wikipedia!