diff --git a/png.d b/png.d index 4c417b6..8b618d1 100644 --- a/png.d +++ b/png.d @@ -138,88 +138,9 @@ MemoryImage imageFromPng(PNG* png) { foreach(line; file.rawDatastreamByChunk()) { auto filter = line[0]; auto data = unfilter(filter, line[1 .. $], previousLine, bpp); - ubyte consumeOne() { - ubyte ret = data[0]; - data = data[1 .. $]; - return ret; - } previousLine = data; - import std.conv; - loop: for(int pixel = 0; pixel < h.width; pixel++) - switch(h.type) { - case 0: // greyscale - case 4: // greyscale with alpha - auto value = consumeOne(); - idata[idataIdx++] = value; - idata[idataIdx++] = value; - idata[idataIdx++] = value; - idata[idataIdx++] = (h.type == 4) ? consumeOne() : 255; - break; - case 3: // indexed - auto b = consumeOne(); - switch(h.depth) { - case 1: - idata[idataIdx++] = (b >> 7) & 0x01; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = (b >> 6) & 0x01; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = (b >> 5) & 0x01; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = (b >> 4) & 0x01; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = (b >> 3) & 0x01; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = (b >> 2) & 0x01; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = (b >> 1) & 0x01; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = b & 0x01; - break; - case 2: - idata[idataIdx++] = (b >> 6) & 0x03; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = (b >> 4) & 0x03; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = (b >> 2) & 0x03; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = b & 0x03; - break; - case 4: - idata[idataIdx++] = (b >> 4) & 0x0f; - pixel++; if(pixel == h.width) break loop; - idata[idataIdx++] = b & 0x0f; - break; - case 8: - idata[idataIdx++] = b; - break; - default: - assert(0, "bit depth not implemented"); - } - break; - case 2: // truecolor - case 6: // true with alpha - if(h.depth == 8) { - idata[idataIdx++] = consumeOne(); - idata[idataIdx++] = consumeOne(); - idata[idataIdx++] = consumeOne(); - idata[idataIdx++] = (h.type == 6) ? consumeOne() : 255; - } else if(h.depth == 16) { - idata[idataIdx++] = consumeOne(); - consumeOne(); - idata[idataIdx++] = consumeOne(); - consumeOne(); - idata[idataIdx++] = consumeOne(); - consumeOne(); - idata[idataIdx++] = (h.type == 6) ? consumeOne() : 255; - if(h.type == 6) - consumeOne(); - - } else assert(0, "unsupported truecolor bit depth " ~ to!string(h.depth)); - break; - default: assert(0); - } - assert(data.length == 0, "not all consumed, wtf " ~ to!string(h)); + convertPngData(h.type, h.depth, data, h.width, idata, idataIdx); } assert(idataIdx == idata.length, "not all filled, wtf"); @@ -228,6 +149,91 @@ MemoryImage imageFromPng(PNG* png) { return i; } +// idata needs to be already sized for the image! width * height if indexed, width*height*4 if not. +void convertPngData(ubyte type, ubyte depth, const(ubyte)[] data, int width, ubyte[] idata, ref int idataIdx) { + ubyte consumeOne() { + ubyte ret = data[0]; + data = data[1 .. $]; + return ret; + } + import std.conv; + + loop: for(int pixel = 0; pixel < width; pixel++) + switch(type) { + case 0: // greyscale + case 4: // greyscale with alpha + auto value = consumeOne(); + idata[idataIdx++] = value; + idata[idataIdx++] = value; + idata[idataIdx++] = value; + idata[idataIdx++] = (type == 4) ? consumeOne() : 255; + break; + case 3: // indexed + auto b = consumeOne(); + switch(depth) { + case 1: + idata[idataIdx++] = (b >> 7) & 0x01; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = (b >> 6) & 0x01; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = (b >> 5) & 0x01; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = (b >> 4) & 0x01; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = (b >> 3) & 0x01; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = (b >> 2) & 0x01; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = (b >> 1) & 0x01; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = b & 0x01; + break; + case 2: + idata[idataIdx++] = (b >> 6) & 0x03; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = (b >> 4) & 0x03; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = (b >> 2) & 0x03; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = b & 0x03; + break; + case 4: + idata[idataIdx++] = (b >> 4) & 0x0f; + pixel++; if(pixel == width) break loop; + idata[idataIdx++] = b & 0x0f; + break; + case 8: + idata[idataIdx++] = b; + break; + default: + assert(0, "bit depth not implemented"); + } + break; + case 2: // truecolor + case 6: // true with alpha + if(depth == 8) { + idata[idataIdx++] = consumeOne(); + idata[idataIdx++] = consumeOne(); + idata[idataIdx++] = consumeOne(); + idata[idataIdx++] = (type == 6) ? consumeOne() : 255; + } else if(depth == 16) { + idata[idataIdx++] = consumeOne(); + consumeOne(); + idata[idataIdx++] = consumeOne(); + consumeOne(); + idata[idataIdx++] = consumeOne(); + consumeOne(); + idata[idataIdx++] = (type == 6) ? consumeOne() : 255; + if(type == 6) + consumeOne(); + + } else assert(0, "unsupported truecolor bit depth " ~ to!string(depth)); + break; + default: assert(0); + } + assert(data.length == 0, "not all consumed, wtf ");// ~ to!string(h)); +} + /* struct PngHeader { uint width; @@ -1467,29 +1473,32 @@ struct LazyPngFile(LazyPngChunksProvider) return .bytesPerPixel(header); } - // FIXME: doesn't handle interlacing... I think int bytesPerLine() { - immutable bitsPerChannel = header.depth; - - int bitsPerPixel = bitsPerChannel; - if(header.type & 2 && !(header.type & 1)) // in color, but no palette - bitsPerPixel *= 3; - if(header.type & 4) // has alpha channel - bitsPerPixel += bitsPerChannel; - - - immutable int sizeInBits = header.width * bitsPerPixel; - - // need to round up to the nearest byte - int sizeInBytes = (sizeInBits + 7) / 8; - - return sizeInBytes + 1; // the +1 is for the filter byte that precedes all lines + return .bytesPerLineOfPng(header.depth, header.type, header.width); } PngHeader header; Color[] palette; } +// FIXME: doesn't handle interlacing... I think +@nogc @safe pure nothrow +int bytesPerLineOfPng(ubyte depth, ubyte type, uint width) { + immutable bitsPerChannel = depth; + + int bitsPerPixel = bitsPerChannel; + if(type & 2 && !(type & 1)) // in color, but no palette + bitsPerPixel *= 3; + if(type & 4) // has alpha channel + bitsPerPixel += bitsPerChannel; + + immutable int sizeInBits = width * bitsPerPixel; + + // need to round up to the nearest byte + int sizeInBytes = (sizeInBits + 7) / 8; + + return sizeInBytes + 1; // the +1 is for the filter byte that precedes all lines +} /************************************************** * Buffered input range - generic, non-image code diff --git a/simpleaudio.d b/simpleaudio.d index 46ab0d0..91b7ac2 100644 --- a/simpleaudio.d +++ b/simpleaudio.d @@ -45,7 +45,7 @@ module arsd.simpleaudio; enum BUFFER_SIZE_FRAMES = 1024;//512;//2048; enum BUFFER_SIZE_SHORT = BUFFER_SIZE_FRAMES * 2; -/// +/// A reasonable default volume for an individual sample. It doesn't need to be large; in fact it needs to not be large so mixing doesn't clip too much. enum DEFAULT_VOLUME = 20; version(Demo)