diff --git a/3rdparty/dimage/array.d b/3rdparty/dimage/array.d
new file mode 100644
index 00000000..68e059a9
--- /dev/null
+++ b/3rdparty/dimage/array.d
@@ -0,0 +1,243 @@
+/*
+Copyright (c) 2015 Timur Gafarov
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.array; //dlib.container.array
+
+import dimage.memory;
+
+/*
+ * GC-free dynamic array implementation.
+ * Very efficient for small-sized arrays.
+ */
+
+struct DynamicArray(T, size_t chunkSize = 32)
+{
+ T[chunkSize] staticStorage;
+ T[] dynamicStorage;
+ uint numChunks = 0;
+ uint pos = 0;
+
+ T* storage()
+ {
+ if (numChunks == 0)
+ return staticStorage.ptr;
+ else
+ return dynamicStorage.ptr;
+ }
+
+ void addChunk()
+ {
+ if (numChunks == 0)
+ {
+ dynamicStorage = New!(T[])(chunkSize);
+ }
+ else
+ {
+ reallocateArray(
+ dynamicStorage,
+ dynamicStorage.length + chunkSize);
+ }
+ numChunks++;
+ }
+
+ void shiftRight()
+ {
+ append(T.init);
+
+ for(uint i = pos-1; i > 0; i--)
+ {
+ storage[i] = storage[i-1];
+ }
+ }
+
+ void shiftLeft(uint n)
+ {
+ for(uint i = 0; i < pos; i++)
+ {
+ if (n + i < pos)
+ storage[i] = storage[n + i];
+ else
+ storage[i] = T.init;
+ }
+ }
+
+ void append(T c)
+ {
+ if (numChunks == 0)
+ {
+ staticStorage[pos] = c;
+ pos++;
+ if (pos == chunkSize)
+ {
+ addChunk();
+ foreach(i, ref v; dynamicStorage)
+ v = staticStorage[i];
+ }
+ }
+ else
+ {
+ if (pos == dynamicStorage.length)
+ addChunk();
+
+ dynamicStorage[pos] = c;
+ pos++;
+ }
+ }
+
+ void appendLeft(T c)
+ {
+ shiftRight();
+ storage[0] = c;
+ }
+
+ void append(const(T)[] s)
+ {
+ foreach(c; s)
+ append(cast(T)c);
+ }
+
+ void appendLeft(const(T)[] s)
+ {
+ foreach(c; s)
+ appendLeft(cast(T)c);
+ }
+
+ auto opCatAssign(T c)
+ {
+ append(c);
+ return this;
+ }
+
+ auto opCatAssign(const(T)[] s)
+ {
+ append(s);
+ return this;
+ }
+
+ uint remove(uint n)
+ {
+ if (pos == n)
+ {
+ pos = 0;
+ return n;
+ }
+ else if (pos >= n)
+ {
+ pos -= n;
+ return n;
+ }
+ else
+ {
+ n = pos;
+ pos = 0;
+ return n;
+ }
+ }
+
+ uint removeLeft(uint n)
+ {
+ if (pos == n)
+ {
+ pos = 0;
+ return n;
+ }
+ else if (pos > n)
+ {
+ shiftLeft(n);
+ pos -= n;
+ return n;
+ }
+ else
+ {
+ n = pos;
+ pos = 0;
+ return n;
+ }
+ }
+
+ size_t length()
+ {
+ return pos;
+ }
+
+ T[] data()
+ {
+ return storage[0..pos];
+ }
+
+ T opIndex(size_t index)
+ {
+ return data[index];
+ }
+
+ T opIndexAssign(T t, size_t index)
+ {
+ data[index] = t;
+ return t;
+ }
+
+ int opApply(int delegate(size_t i, ref T) dg)
+ {
+ foreach(i, ref v; data)
+ {
+ dg(i, v);
+ }
+
+ return 0;
+ }
+
+ int opApply(int delegate(ref T) dg)
+ {
+ foreach(i, ref v; data)
+ {
+ dg(v);
+ }
+
+ return 0;
+ }
+
+ void free()
+ {
+ if (dynamicStorage.length)
+ Delete(dynamicStorage);
+ numChunks = 0;
+ pos = 0;
+ }
+}
+
+void reallocateArray(T)(ref T[] buffer, size_t len)
+{
+ T[] buffer2 = New!(T[])(len);
+ for(uint i = 0; i < buffer2.length; i++)
+ if (i < buffer.length)
+ buffer2[i] = buffer[i];
+ Delete(buffer);
+ buffer = buffer2;
+}
+
diff --git a/3rdparty/dimage/bitio.d b/3rdparty/dimage/bitio.d
new file mode 100644
index 00000000..83163ffe
--- /dev/null
+++ b/3rdparty/dimage/bitio.d
@@ -0,0 +1,70 @@
+/*
+Copyright (c) 2015 Timur Gafarov
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.bitio; //dlib.core.bitio
+
+/*
+ * Bit-level manipulations
+ */
+
+enum Endian
+{
+ Little,
+ Big
+}
+
+T hiNibble(T)(T b)
+{
+ return ((b >> 4) & 0x0F);
+}
+
+T loNibble(T)(T b)
+{
+ return (b & 0x0F);
+}
+
+T swapEndian16(T)(T n)
+{
+ return cast(T)((n >> 8) | (n << 8));
+}
+
+T setBit(T)(T b, uint pos, bool state)
+{
+ if (state)
+ return cast(T)(b | (1 << pos));
+ else
+ return cast(T)(b & ~(1 << pos));
+}
+
+bool getBit(T)(T b, uint pos)
+{
+ return ((b & (1 << pos)) != 0);
+}
+
+
diff --git a/3rdparty/dimage/compound.d b/3rdparty/dimage/compound.d
new file mode 100644
index 00000000..832a86c3
--- /dev/null
+++ b/3rdparty/dimage/compound.d
@@ -0,0 +1,43 @@
+/*
+Copyright (c) 2011-2013 Timur Gafarov
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+//dlib.core.compound
+module dimage.compound;
+
+struct Compound(T...)
+{
+ T tuple;
+ alias tuple this;
+}
+
+Compound!(T) compound(T...)(T args)
+{
+ return Compound!(T)(args);
+}
+
diff --git a/3rdparty/dimage/huffman.d b/3rdparty/dimage/huffman.d
new file mode 100644
index 00000000..2019a1ad
--- /dev/null
+++ b/3rdparty/dimage/huffman.d
@@ -0,0 +1,309 @@
+/*
+Copyright (c) 2015 Timur Gafarov
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.huffman;
+
+import dimage.memory;
+import dimage.bitio;
+
+//import dlib.core.memory;
+//import dlib.core.bitio;
+
+struct HuffmanTreeNode
+{
+ HuffmanTreeNode* parent;
+ HuffmanTreeNode* left;
+ HuffmanTreeNode* right;
+ ubyte ch;
+ uint freq;
+ bool blank = true;
+
+ this(
+ HuffmanTreeNode* leftNode,
+ HuffmanTreeNode* rightNode,
+ ubyte symbol,
+ uint frequency,
+ bool isBlank)
+ {
+ parent = null;
+ left = leftNode;
+ right = rightNode;
+
+ if (left !is null)
+ left.parent = &this;
+ if (right !is null)
+ right.parent = &this;
+
+ ch = symbol;
+ freq = frequency;
+ blank = isBlank;
+ }
+
+ bool isLeaf()
+ {
+ return (left is null && right is null);
+ }
+
+ void free()
+ {
+ if (left !is null)
+ {
+ left.free();
+ Delete(left);
+ }
+
+ if (right !is null)
+ {
+ right.free();
+ Delete(right);
+ }
+ }
+
+/*
+ // TODO: implement this without GC
+
+ void getCodes(ref string[ubyte] table, string code = "")
+ {
+ if (isLeaf())
+ {
+ table[ch] = code;
+ }
+ else
+ {
+ if (left !is null)
+ left.getCodes(table, code ~ '0');
+ if (right !is null)
+ right.getCodes(table, code ~ '1');
+ }
+ }
+
+ void print(string indent = "")
+ {
+ writefln("%s<%s>%x", indent, freq, ch);
+ indent ~= " ";
+ if (left !is null)
+ left.print(indent);
+ if (right !is null)
+ right.print(indent);
+ }
+*/
+}
+
+/*
+// TODO: implement this without GC
+
+HuffmanTreeNode* buildHuffmanTree(ubyte[] data)
+{
+ // Count frequencies
+ uint[ubyte] freqs;
+ foreach(s; data)
+ {
+ if (s in freqs)
+ freqs[s] += 1;
+ else
+ freqs[s] = 1;
+ }
+
+ // Sort in descending order
+ ubyte[] symbols = freqs.keys;
+ sort!((a, b) => freqs[a] > freqs[b])(symbols);
+
+ // Create node list
+ auto nodeList = new HuffmanTreeNode*[symbols.length];
+ foreach(i, s; symbols)
+ nodeList[i] = new HuffmanTreeNode(null, null, s, freqs[s], false);
+
+ // Build tree
+ while (nodeList.length > 1)
+ {
+ // Pop two nodes with minimal frequencies
+ auto n1 = nodeList[$-1];
+ auto n2 = nodeList[$-2];
+ nodeList.popBack;
+ nodeList.popBack;
+
+ // Insert a new parent node
+ uint fsum = n1.freq + n2.freq;
+ auto parent = new HuffmanTreeNode(n1, n2, 0, fsum, false);
+ nodeList ~= parent;
+ sort!((a, b) => a.freq > b.freq)(nodeList);
+ }
+
+ auto root = nodeList[0];
+
+ return root;
+}
+
+void packHuffmanTree(HuffmanTreeNode* node, BitWriter* bw)
+{
+ if (node.isLeaf)
+ {
+ bw.writeBit(true);
+ bw.writeByte(node.ch);
+ }
+ else
+ {
+ bw.writeBit(false);
+ packHuffmanTree(node.left, bw);
+ packHuffmanTree(node.right, bw);
+ }
+}
+
+HuffmanTreeNode* unpackHuffmanTree(BitReader* br)
+{
+ if (!br.end)
+ {
+ bool bit = br.readBit();
+ if (bit)
+ {
+ byte ch = br.readByte();
+ return new HuffmanTreeNode(null, null, ch, 0, false);
+ }
+ else
+ {
+ HuffmanTreeNode* left = unpackHuffmanTree(br);
+ HuffmanTreeNode* right = unpackHuffmanTree(br);
+ return new HuffmanTreeNode(left, right, 0, 0, false);
+ }
+ }
+ else return null;
+}
+
+ubyte[] encodeHuffman(ubyte[] data, out HuffmanTreeNode* tree)
+{
+ // Build Huffman tree
+ tree = buildHuffmanTree(data);
+
+ // Generate binary codes
+ string[ubyte] huffTable;
+ tree.getCodes(huffTable);
+
+ // Encode data
+ string bitStr;
+ foreach(s; data)
+ bitStr ~= huffTable[s];
+
+ // Pack bits to byte array
+ uint octetsLen = 0;
+ ubyte lastBits = 0;
+ if (bitStr.length == 8)
+ {
+ octetsLen = 1;
+ }
+ else if (bitStr.length > 8)
+ {
+ octetsLen = cast(uint)bitStr.length / 8;
+ lastBits = cast(ubyte)(bitStr.length % 8);
+ if (lastBits != 0)
+ octetsLen++;
+ }
+ else
+ {
+ octetsLen = 1;
+ lastBits = cast(ubyte)(bitStr.length);
+ }
+
+ octetsLen++;
+ auto octets = new ubyte[octetsLen];
+ octets[0] = lastBits;
+
+ uint bitPos = 0;
+ uint bytePos = 1;
+
+ foreach(bit; bitStr)
+ {
+ bool state;
+ if (bit == '0')
+ state = false;
+ else
+ state = true;
+
+ octets[bytePos] = setBit(octets[bytePos], bitPos, state);
+ bitPos++;
+
+ if (bitPos == 8)
+ {
+ bitPos = 0;
+ bytePos++;
+ }
+ }
+
+ return octets;
+}
+
+ubyte[] decodeHuffman(ubyte[] data, HuffmanTreeNode* tree)
+{
+ // Generate binary codes
+ string[ubyte] huffTable;
+ tree.getCodes(huffTable);
+
+ //Unpack bits from array
+ ubyte[] result;
+ bool appendNext = true;
+ string code = "";
+ ubyte lastBits = data[0];
+ foreach(i, b; data[1..$])
+ {
+ uint len;
+ if ((lastBits != 0) && (i == data.length-1))
+ len = lastBits;
+ else
+ len = 8;
+
+ foreach(bp; 0..len)
+ {
+ char bitChr = getBit(b, bp)? '1':'0';
+ if (appendNext)
+ {
+ code ~= bitChr;
+ foreach(key, val; huffTable)
+ {
+ if (code == val)
+ {
+ result ~= key;
+ appendNext = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ code = "";
+ code ~= bitChr;
+ appendNext = true;
+ }
+ }
+ }
+
+ return result;
+}
+
+*/
+
+
diff --git a/3rdparty/dimage/idct.d b/3rdparty/dimage/idct.d
new file mode 100644
index 00000000..534cd98f
--- /dev/null
+++ b/3rdparty/dimage/idct.d
@@ -0,0 +1,217 @@
+/*
+Copyright (c) 2014 Timur Gafarov
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.idct; //dlib.image.io.idct
+
+import std.math;
+
+/*
+ * Inverse discrete cosine transform (DCT) for 64x64 blocks
+ */
+
+enum blockSize = 64; // A DCT block is 8x8.
+
+enum w1 = 2841; // 2048*sqrt(2)*cos(1*pi/16)
+enum w2 = 2676; // 2048*sqrt(2)*cos(2*pi/16)
+enum w3 = 2408; // 2048*sqrt(2)*cos(3*pi/16)
+enum w5 = 1609; // 2048*sqrt(2)*cos(5*pi/16)
+enum w6 = 1108; // 2048*sqrt(2)*cos(6*pi/16)
+enum w7 = 565; // 2048*sqrt(2)*cos(7*pi/16)
+
+enum w1pw7 = w1 + w7;
+enum w1mw7 = w1 - w7;
+enum w2pw6 = w2 + w6;
+enum w2mw6 = w2 - w6;
+enum w3pw5 = w3 + w5;
+enum w3mw5 = w3 - w5;
+
+enum r2 = 181; // 256/sqrt(2)
+
+// idct performs a 2-D Inverse Discrete Cosine Transformation.
+//
+// The input coefficients should already have been multiplied by the
+// appropriate quantization table. We use fixed-point computation, with the
+// number of bits for the fractional component varying over the intermediate
+// stages.
+//
+// For more on the actual algorithm, see Z. Wang, "Fast algorithms for the
+// discrete W transform and for the discrete Fourier transform", IEEE Trans. on
+// ASSP, Vol. ASSP- 32, pp. 803-816, Aug. 1984.
+void idct64(int* src)
+{
+ // Horizontal 1-D IDCT.
+ for (uint y = 0; y < 8; y++)
+ {
+ int y8 = y * 8;
+ // If all the AC components are zero, then the IDCT is trivial.
+ if (src[y8+1] == 0 && src[y8+2] == 0 && src[y8+3] == 0 &&
+ src[y8+4] == 0 && src[y8+5] == 0 && src[y8+6] == 0 && src[y8+7] == 0)
+ {
+ int dc = src[y8+0] << 3;
+ src[y8+0] = dc;
+ src[y8+1] = dc;
+ src[y8+2] = dc;
+ src[y8+3] = dc;
+ src[y8+4] = dc;
+ src[y8+5] = dc;
+ src[y8+6] = dc;
+ src[y8+7] = dc;
+ continue;
+ }
+
+ // Prescale.
+ int x0 = (src[y8+0] << 11) + 128;
+ int x1 = src[y8+4] << 11;
+ int x2 = src[y8+6];
+ int x3 = src[y8+2];
+ int x4 = src[y8+1];
+ int x5 = src[y8+7];
+ int x6 = src[y8+5];
+ int x7 = src[y8+3];
+
+ // Stage 1.
+ int x8 = w7 * (x4 + x5);
+ x4 = x8 + w1mw7*x4;
+ x5 = x8 - w1pw7*x5;
+ x8 = w3 * (x6 + x7);
+ x6 = x8 - w3mw5*x6;
+ x7 = x8 - w3pw5*x7;
+
+ // Stage 2.
+ x8 = x0 + x1;
+ x0 -= x1;
+ x1 = w6 * (x3 + x2);
+ x2 = x1 - w2pw6*x2;
+ x3 = x1 + w2mw6*x3;
+ x1 = x4 + x6;
+ x4 -= x6;
+ x6 = x5 + x7;
+ x5 -= x7;
+
+ // Stage 3.
+ x7 = x8 + x3;
+ x8 -= x3;
+ x3 = x0 + x2;
+ x0 -= x2;
+ x2 = (r2*(x4+x5) + 128) >> 8;
+ x4 = (r2*(x4-x5) + 128) >> 8;
+
+ // Stage 4.
+ src[y8+0] = (x7 + x1) >> 8;
+ src[y8+1] = (x3 + x2) >> 8;
+ src[y8+2] = (x0 + x4) >> 8;
+ src[y8+3] = (x8 + x6) >> 8;
+ src[y8+4] = (x8 - x6) >> 8;
+ src[y8+5] = (x0 - x4) >> 8;
+ src[y8+6] = (x3 - x2) >> 8;
+ src[y8+7] = (x7 - x1) >> 8;
+ }
+
+ // Vertical 1-D IDCT.
+ for (uint x = 0; x < 8; x++)
+ {
+ // Similar to the horizontal 1-D IDCT case, if all the AC components are zero, then the IDCT is trivial.
+ // However, after performing the horizontal 1-D IDCT, there are typically non-zero AC components, so
+ // we do not bother to check for the all-zero case.
+
+ // Prescale.
+ int y0 = (src[8*0+x] << 8) + 8192;
+ int y1 = src[8*4+x] << 8;
+ int y2 = src[8*6+x];
+ int y3 = src[8*2+x];
+ int y4 = src[8*1+x];
+ int y5 = src[8*7+x];
+ int y6 = src[8*5+x];
+ int y7 = src[8*3+x];
+
+ // Stage 1.
+ int y8 = w7*(y4+y5) + 4;
+ y4 = (y8 + w1mw7*y4) >> 3;
+ y5 = (y8 - w1pw7*y5) >> 3;
+ y8 = w3*(y6+y7) + 4;
+ y6 = (y8 - w3mw5*y6) >> 3;
+ y7 = (y8 - w3pw5*y7) >> 3;
+
+ // Stage 2.
+ y8 = y0 + y1;
+ y0 -= y1;
+ y1 = w6*(y3+y2) + 4;
+ y2 = (y1 - w2pw6*y2) >> 3;
+ y3 = (y1 + w2mw6*y3) >> 3;
+ y1 = y4 + y6;
+ y4 -= y6;
+ y6 = y5 + y7;
+ y5 -= y7;
+
+ // Stage 3.
+ y7 = y8 + y3;
+ y8 -= y3;
+ y3 = y0 + y2;
+ y0 -= y2;
+ y2 = (r2*(y4+y5) + 128) >> 8;
+ y4 = (r2*(y4-y5) + 128) >> 8;
+
+ // Stage 4.
+ src[8*0+x] = (y7 + y1) >> 14;
+ src[8*1+x] = (y3 + y2) >> 14;
+ src[8*2+x] = (y0 + y4) >> 14;
+ src[8*3+x] = (y8 + y6) >> 14;
+ src[8*4+x] = (y8 - y6) >> 14;
+ src[8*5+x] = (y0 - y4) >> 14;
+ src[8*6+x] = (y3 - y2) >> 14;
+ src[8*7+x] = (y7 - y1) >> 14;
+ }
+}
+
+/*
+void idct(float* inMat, float* outMat)
+{
+ uint i, j, u, v;
+ float s;
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ {
+ s = 0;
+
+ for (u = 0; u < 8; u++)
+ for (v = 0; v < 8; v++)
+ {
+ s += inMat[u * 8 + v]
+ * cos((2 * i + 1) * u * PI / 16.0f)
+ * cos((2 * j + 1) * v * PI / 16.0f)
+ * ((u == 0) ? 1 / sqrt(2.0f) : 1.0f)
+ * ((v == 0) ? 1 / sqrt(2.0f) : 1.0f);
+ }
+
+ outMat[i * 8 + j] = s / 4.0f;
+ }
+}
+*/
+
diff --git a/3rdparty/dimage/image.d b/3rdparty/dimage/image.d
new file mode 100644
index 00000000..5b4c97d8
--- /dev/null
+++ b/3rdparty/dimage/image.d
@@ -0,0 +1,83 @@
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.image;
+
+//import dimage.color;
+
+class ImageLoadException : Exception
+{
+ this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
+ {
+ super(msg, file, line, next);
+ }
+}
+
+
+class SuperImageFactory {
+ SuperImage createImage(int width, int height, int components, int bitsPerComponent) {
+ return new SuperImage(width, height, components, bitsPerComponent);
+ }
+}
+
+class SuperImage {
+ immutable int width;
+ immutable int height;
+ uint[] data;
+ immutable int channels;
+ immutable int bitDepth;
+ immutable int length;
+
+ void opIndexAssign(uint color, int x, int y) {
+ data[x + y * width] = color;
+ }
+
+ uint opIndex(int x, int y) {
+ return data[x + y * width];
+ }
+
+ this(int w, int h, int chan, int depth) {
+ width = w;
+ height = h;
+ channels = chan;
+ bitDepth = depth;
+ length = width * height;
+ data.length = width * height;
+ }
+ void free() {
+ data = null;
+ }
+}
+
+__gshared SuperImageFactory defaultImageFactory = new SuperImageFactory();
+
+
+/*
+ * Byte operations
+ */
+version (BigEndian)
+{
+ uint bigEndian(uint value) nothrow
+ {
+ return value;
+ }
+
+ uint networkByteOrder(uint value) nothrow
+ {
+ return value;
+ }
+}
+
+version (LittleEndian)
+{
+ uint bigEndian(uint value) nothrow
+ {
+ return value << 24
+ | (value & 0x0000FF00) << 8
+ | (value & 0x00FF0000) >> 8
+ | value >> 24;
+ }
+
+ uint networkByteOrder(uint value) nothrow
+ {
+ return bigEndian(value);
+ }
+}
diff --git a/3rdparty/dimage/jpeg.d b/3rdparty/dimage/jpeg.d
new file mode 100644
index 00000000..af7f0a59
--- /dev/null
+++ b/3rdparty/dimage/jpeg.d
@@ -0,0 +1,1306 @@
+/*
+Copyright (c) 2015 Timur Gafarov
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.jpeg;
+
+version = USE_DIMAGE;
+version(USE_DIMAGE):
+
+
+import std.stdio;
+import std.algorithm;
+import std.string;
+import std.traits;
+
+import dimage.huffman;
+import dimage.stream;
+import dimage.compound;
+import dimage.array;
+//import dimage.color;
+import dimage.image;
+import dimage.bitio;
+import dimage.memory;
+import dimage.idct;
+//import dlib.core.memory;
+//import dlib.core.stream;
+//import dlib.core.compound;
+//import dlib.container.array;
+//import dlib.filesystem.local;
+//import dlib.image.color;
+//import dlib.image.image;
+//import dlib.image.io.idct;
+
+//import dlib.core.bitio;
+//import dlib.coding.huffman;
+
+/*
+ * Simple JPEG decoder
+ *
+ * Limitations:
+ * - Doesn't support progressive JPEG
+ * - Doesn't perform chroma interpolation
+ * - Doesn't read EXIF metadata
+ */
+
+// Uncomment this to see debug messages
+//version = JPEGDebug;
+
+T readNumeric(T) (InputStream istrm, Endian endian = Endian.Little)
+if (is(T == ubyte))
+{
+ ubyte b;
+ istrm.readBytes(&b, 1);
+ return b;
+}
+
+T readNumeric(T) (InputStream istrm, Endian endian = Endian.Little)
+if (is(T == ushort))
+{
+ union U16
+ {
+ ubyte[2] asBytes;
+ ushort asUshort;
+ }
+ U16 u16;
+ istrm.readBytes(u16.asBytes.ptr, 2);
+ version(LittleEndian)
+ {
+ if (endian == Endian.Big)
+ return u16.asUshort.swapEndian16;
+ else
+ return u16.asUshort;
+ }
+ else
+ {
+ if (endian == Endian.Little)
+ return u16.asUshort.swapEndian16;
+ else
+ return u16.asUshort;
+ }
+}
+
+char[size] readChars(size_t size) (InputStream istrm)
+{
+ char[size] chars;
+ istrm.readBytes(chars.ptr, size);
+ return chars;
+}
+
+/*
+ * JPEG-related Huffman coding
+ */
+
+struct HuffmanCode
+{
+ ushort bits;
+ ushort length;
+
+ auto bitString()
+ {
+ return .bitString(bits, length);
+ }
+}
+
+struct HuffmanTableEntry
+{
+ HuffmanCode code;
+ ubyte value;
+}
+
+DynamicArray!char bitString(T)(T n, uint len = 1) if (isIntegral!T)
+{
+ DynamicArray!char arr;
+
+ const int size = T.sizeof * 8;
+
+ bool s = 0;
+ for (int a = 0; a < size; a++)
+ {
+ bool bit = n >> (size - 1);
+ if (bit)
+ s = 1;
+ if (s)
+ {
+ arr.append(bit + '0');
+ }
+ n <<= 1;
+ }
+
+ while (arr.length < len)
+ arr.appendLeft('0');
+
+ return arr;
+}
+
+HuffmanTreeNode* emptyNode()
+{
+ return New!HuffmanTreeNode(null, null, cast(ubyte)0, 0, false);
+}
+
+HuffmanTreeNode* treeFromTable(DynamicArray!(HuffmanTableEntry) table)
+{
+ HuffmanTreeNode* root = emptyNode();
+
+ foreach(i, v; table.data)
+ treeAddCode(root, v.code, v.value);
+
+ return root;
+}
+
+void treeAddCode(HuffmanTreeNode* root, HuffmanCode code, ubyte value)
+{
+ HuffmanTreeNode* node = root;
+ auto bs = code.bitString;
+ foreach(bit; bs.data)
+ {
+ if (bit == '0')
+ {
+ if (node.left is null)
+ {
+ node.left = emptyNode();
+ node.left.parent = node;
+ }
+
+ node = node.left;
+ }
+ else if (bit == '1')
+ {
+ if (node.right is null)
+ {
+ node.right = emptyNode();
+ node.right.parent = node;
+ }
+
+ node = node.right;
+ }
+ }
+ assert (node !is null);
+ node.ch = value;
+ bs.free();
+}
+
+/*
+ * JPEG-related data types
+ */
+
+enum JPEGMarkerType
+{
+ Unknown,
+ SOI,
+ SOF0,
+ SOF1,
+ SOF2,
+ DHT,
+ DQT,
+ DRI,
+ SOS,
+ RSTn,
+ APP0,
+ APPn,
+ COM,
+ EOI
+}
+
+struct JPEGImage
+{
+ struct JFIF
+ {
+ ubyte versionMajor;
+ ubyte versionMinor;
+ ubyte units;
+ ushort xDensity;
+ ushort yDensity;
+ ubyte thumbnailWidth;
+ ubyte thumbnailHeight;
+ ubyte[] thumbnail;
+
+ void free()
+ {
+ if (thumbnail.length)
+ Delete(thumbnail);
+ }
+ }
+
+ struct DQT
+ {
+ ubyte precision;
+ ubyte tableId;
+ ubyte[] table;
+
+ void free()
+ {
+ if (table.length)
+ Delete(table);
+ }
+ }
+
+ struct SOF0Component
+ {
+ ubyte hSubsampling;
+ ubyte vSubsampling;
+ ubyte dqtTableId;
+ }
+
+ struct SOF0
+ {
+ ubyte precision;
+ ushort height;
+ ushort width;
+ ubyte componentsNum;
+ SOF0Component[] components;
+
+ void free()
+ {
+ if (components.length)
+ Delete(components);
+ }
+ }
+
+ struct DHT
+ {
+ ubyte clas;
+ ubyte tableId;
+ DynamicArray!HuffmanTableEntry huffmanTable;
+ HuffmanTreeNode* huffmanTree;
+
+ void free()
+ {
+ huffmanTree.free();
+ Delete(huffmanTree);
+ huffmanTable.free();
+ }
+ }
+
+ struct SOSComponent
+ {
+ ubyte tableIdDC;
+ ubyte tableIdAC;
+ }
+
+ struct SOS
+ {
+ ubyte componentsNum;
+ SOSComponent[] components;
+ ubyte spectralSelectionStart;
+ ubyte spectralSelectionEnd;
+ ubyte successiveApproximationBitHigh;
+ ubyte successiveApproximationBitLow;
+
+ void free()
+ {
+ if (components.length)
+ Delete(components);
+ }
+ }
+
+ JFIF jfif;
+ DQT[] dqt;
+ SOF0 sof0;
+ DHT[] dht;
+ SOS sos;
+
+ DQT* addDQT()
+ {
+ if (dqt.length > 0)
+ reallocateArray(dqt, dqt.length+1);
+ else
+ dqt = New!(DQT[])(1);
+ return &dqt[$-1];
+ }
+
+ DHT* addDHT()
+ {
+ if (dht.length > 0)
+ reallocateArray(dht, dht.length+1);
+ else
+ dht = New!(DHT[])(1);
+ return &dht[$-1];
+ }
+
+ void free()
+ {
+ jfif.free();
+ foreach(ref t; dqt) t.free();
+ Delete(dqt);
+ sof0.free();
+ foreach(ref t; dht) t.free();
+ Delete(dht);
+ sos.free();
+ }
+
+ DQT* getQuantizationTable(ubyte id)
+ {
+ foreach(ref t; dqt)
+ if (t.tableId == id)
+ return &t;
+ return null;
+ }
+
+ DHT* getHuffmanTable(ubyte clas, ubyte id)
+ {
+ foreach(ref t; dht)
+ if (t.clas == clas &&
+ t.tableId == id)
+ return &t;
+ return null;
+ }
+}
+
+/*
+ * Load JPEG from file using local FileSystem.
+ * Causes GC allocation
+ */
+//SuperImage loadJPEG(string filename)
+//{
+// InputStream input = openForInput(filename);
+// auto img = loadJPEG(input);
+// input.close();
+// return img;
+//}
+
+/*
+ * Load JPEG from stream using default image factory.
+ * Causes GC allocation
+ */
+SuperImage loadJPEG(InputStream istrm)
+{
+ Compound!(SuperImage, string) res =
+ loadJPEG(istrm, defaultImageFactory);
+ if (res[0] is null)
+ throw new Exception(res[1]);
+ else
+ return res[0];
+}
+
+/*
+ * Load JPEG from stream using specified image factory.
+ * GC-free
+ */
+Compound!(SuperImage, string) loadJPEG(
+ InputStream istrm,
+ SuperImageFactory imgFac)
+{
+ JPEGImage jpg;
+ SuperImage img = null;
+
+ while (istrm.readable)
+ {
+ JPEGMarkerType mt;
+ auto res = readMarker(&jpg, istrm, &mt);
+ if (res[0])
+ {
+ // TODO: add progressive JPEG support
+ if (mt == JPEGMarkerType.SOF2)
+ {
+ jpg.free();
+ return compound(img, "loadJPEG error: progressive JPEG is not supported");
+ }
+ else if (mt == JPEGMarkerType.SOS)
+ break;
+ }
+ else
+ {
+ jpg.free();
+ return compound(img, res[1]);
+ }
+ }
+ auto res = decodeScanData(&jpg, istrm, imgFac);
+ jpg.free();
+ return res;
+}
+
+/*
+ * Decode marker from JPEG stream
+ */
+Compound!(bool, string) readMarker(
+ JPEGImage* jpg,
+ InputStream istrm,
+ JPEGMarkerType* mt)
+{
+ ushort magic = istrm.readNumeric!ushort(Endian.Big);
+
+ switch (magic)
+ {
+ case 0xFFD8:
+ *mt = JPEGMarkerType.SOI;
+ version(JPEGDebug) writeln("SOI");
+ break;
+
+ case 0xFFE0:
+ *mt = JPEGMarkerType.APP0;
+ return readJFIF(jpg, istrm);
+
+ case 0xFFE1:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 1);
+
+ case 0xFFE2:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 2);
+
+ case 0xFFE3:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 3);
+
+ case 0xFFE4:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 4);
+
+ case 0xFFE5:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 5);
+
+ case 0xFFE6:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 6);
+
+ case 0xFFE7:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 7);
+
+ case 0xFFE8:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 8);
+
+ case 0xFFE9:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 9);
+
+ case 0xFFEA:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 10);
+
+ case 0xFFEB:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 11);
+
+ case 0xFFEC:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 12);
+
+ case 0xFFED:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 13);
+
+ case 0xFFEE:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 14);
+
+ case 0xFFEF:
+ *mt = JPEGMarkerType.APPn;
+ return readAPPn(jpg, istrm, 15);
+
+ case 0xFFDB:
+ *mt = JPEGMarkerType.DQT;
+ return readDQT(jpg, istrm);
+
+ case 0xFFC0:
+ *mt = JPEGMarkerType.SOF0;
+ return readSOF0(jpg, istrm);
+
+ case 0xFFC2:
+ *mt = JPEGMarkerType.SOF2;
+ break;
+
+ case 0xFFC4:
+ *mt = JPEGMarkerType.DHT;
+ return readDHT(jpg, istrm);
+
+ case 0xFFDA:
+ *mt = JPEGMarkerType.SOS;
+ return readSOS(jpg, istrm);
+
+ case 0xFFFE:
+ *mt = JPEGMarkerType.COM;
+ return readCOM(jpg, istrm);
+
+ default:
+ *mt = JPEGMarkerType.Unknown;
+ break;
+ }
+
+ return compound(true, "");
+}
+
+Compound!(bool, string) readJFIF(JPEGImage* jpg, InputStream istrm)
+{
+ ushort jfif_length = istrm.readNumeric!ushort(Endian.Big);
+
+ char[5] jfif_id = istrm.readChars!5;
+ if (jfif_id != "JFIF\0")
+ return compound(false, "loadJPEG error: illegal JFIF header");
+
+ jpg.jfif.versionMajor = istrm.readNumeric!ubyte;
+ jpg.jfif.versionMinor = istrm.readNumeric!ubyte;
+ jpg.jfif.units = istrm.readNumeric!ubyte;
+ jpg.jfif.xDensity = istrm.readNumeric!ushort(Endian.Big);
+ jpg.jfif.yDensity = istrm.readNumeric!ushort(Endian.Big);
+ jpg.jfif.thumbnailWidth = istrm.readNumeric!ubyte;
+ jpg.jfif.thumbnailHeight = istrm.readNumeric!ubyte;
+
+ uint jfif_thumb_length = jpg.jfif.thumbnailWidth * jpg.jfif.thumbnailHeight * 3;
+ if (jfif_thumb_length > 0)
+ {
+ jpg.jfif.thumbnail = New!(ubyte[])(jfif_thumb_length);
+ istrm.readBytes(jpg.jfif.thumbnail.ptr, jfif_thumb_length);
+ }
+
+ version(JPEGDebug)
+ {
+ writefln("APP0/JFIF length: %s", jfif_length);
+ writefln("APP0/JFIF identifier: %s", jfif_id);
+ writefln("APP0/JFIF version major: %s", jpg.jfif.versionMajor);
+ writefln("APP0/JFIF version minor: %s", jpg.jfif.versionMinor);
+ writefln("APP0/JFIF units: %s", jpg.jfif.units);
+ writefln("APP0/JFIF xdensity: %s", jpg.jfif.xDensity);
+ writefln("APP0/JFIF ydensity: %s", jpg.jfif.yDensity);
+ writefln("APP0/JFIF xthumbnail: %s", jpg.jfif.thumbnailWidth);
+ writefln("APP0/JFIF ythumbnail: %s", jpg.jfif.thumbnailHeight);
+ }
+
+ return compound(true, "");
+}
+
+/*
+ * APP1 - EXIF, XMP, ExtendedXMP, QVCI, FLIR
+ * APP2 - ICC, FPXR, MPF, PreviewImage
+ * APP3 - Kodak Meta, Stim, PreviewImage
+ * APP4 - Scalado, FPXR, PreviewImage
+ * APP5 - RMETA, PreviewImage
+ * APP6 - EPPIM, NITF, HP TDHD
+ * APP7 - Pentax, Qualcomm
+ * APP8 - SPIFF
+ * APP9 - MediaJukebox
+ * APP10 - PhotoStudio comment
+ * APP11 - JPEG-HDR
+ * APP12 - PictureInfo, Ducky
+ * APP13 - Photoshop, Adobe CM
+ * APP14 - Adobe
+ * APP15 - GraphicConverter
+ */
+Compound!(bool, string) readAPPn(JPEGImage* jpg, InputStream istrm, uint n)
+{
+ ushort app_length = istrm.readNumeric!ushort(Endian.Big);
+ ubyte[] app = New!(ubyte[])(app_length-2);
+ istrm.readBytes(app.ptr, app_length-2);
+
+ // TODO: interpret APP data (EXIF etc.) and save it somewhere.
+ // Maybe add a generic ImageInfo object for this?
+ Delete(app);
+
+ version(JPEGDebug)
+ {
+ writefln("APP%s length: %s", n, app_length);
+ }
+
+ return compound(true, "");
+}
+
+Compound!(bool, string) readCOM(JPEGImage* jpg, InputStream istrm)
+{
+ ushort com_length = istrm.readNumeric!ushort(Endian.Big);
+ ubyte[] com = New!(ubyte[])(com_length-2);
+ istrm.readBytes(com.ptr, com_length-2);
+
+ version(JPEGDebug)
+ {
+ writefln("COM string: \"%s\"", cast(string)com);
+ writefln("COM length: %s", com_length);
+ }
+
+ // TODO: save COM data somewhere.
+ // Maybe add a generic ImageInfo object for this?
+ Delete(com);
+
+ return compound(true, "");
+}
+
+Compound!(bool, string) readDQT(JPEGImage* jpg, InputStream istrm)
+{
+ ushort dqt_length = istrm.readNumeric!ushort(Endian.Big);
+ version(JPEGDebug)
+ {
+ writefln("DQT length: %s", dqt_length);
+ }
+
+ dqt_length -= 2;
+
+ while(dqt_length)
+ {
+ JPEGImage.DQT* dqt = jpg.addDQT();
+
+ ubyte bite = istrm.readNumeric!ubyte;
+ dqt.precision = bite.hiNibble;
+ dqt.tableId = bite.loNibble;
+
+ dqt_length--;
+
+ if (dqt.precision == 0)
+ {
+ dqt.table = New!(ubyte[])(64);
+ dqt_length -= 64;
+ }
+ else if (dqt.precision == 1)
+ {
+ dqt.table = New!(ubyte[])(128);
+ dqt_length -= 128;
+ }
+
+ istrm.readBytes(dqt.table.ptr, dqt.table.length);
+
+ version(JPEGDebug)
+ {
+ writefln("DQT precision: %s", dqt.precision);
+ writefln("DQT table id: %s", dqt.tableId);
+ writefln("DQT table: %s", dqt.table);
+ }
+ }
+
+ return compound(true, "");
+}
+
+Compound!(bool, string) readSOF0(JPEGImage* jpg, InputStream istrm)
+{
+ ushort sof0_length = istrm.readNumeric!ushort(Endian.Big);
+ jpg.sof0.precision = istrm.readNumeric!ubyte;
+ jpg.sof0.height = istrm.readNumeric!ushort(Endian.Big);
+ jpg.sof0.width = istrm.readNumeric!ushort(Endian.Big);
+ jpg.sof0.componentsNum = istrm.readNumeric!ubyte;
+
+ version(JPEGDebug)
+ {
+ writefln("SOF0 length: %s", sof0_length);
+ writefln("SOF0 precision: %s", jpg.sof0.precision);
+ writefln("SOF0 height: %s", jpg.sof0.height);
+ writefln("SOF0 width: %s", jpg.sof0.width);
+ writefln("SOF0 components: %s", jpg.sof0.componentsNum);
+ }
+
+ jpg.sof0.components = New!(JPEGImage.SOF0Component[])(jpg.sof0.componentsNum);
+
+ foreach(ref c; jpg.sof0.components)
+ {
+ ubyte c_id = istrm.readNumeric!ubyte;
+ ubyte bite = istrm.readNumeric!ubyte;
+ c.hSubsampling = bite.hiNibble;
+ c.vSubsampling = bite.loNibble;
+ c.dqtTableId = istrm.readNumeric!ubyte;
+ version(JPEGDebug)
+ {
+ writefln("SOF0 component id: %s", c_id);
+ writefln("SOF0 component %s hsubsampling: %s", c_id, c.hSubsampling);
+ writefln("SOF0 component %s vsubsampling: %s", c_id, c.vSubsampling);
+ writefln("SOF0 component %s table id: %s", c_id, c.dqtTableId);
+ }
+ }
+
+ return compound(true, "");
+}
+
+Compound!(bool, string) readDHT(JPEGImage* jpg, InputStream istrm)
+{
+ ushort dht_length = istrm.readNumeric!ushort(Endian.Big);
+ version(JPEGDebug)
+ {
+ writefln("DHT length: %s", dht_length);
+ }
+
+ dht_length -= 2;
+
+ while(dht_length > 0)
+ {
+ JPEGImage.DHT* dht = jpg.addDHT();
+
+ ubyte bite = istrm.readNumeric!ubyte;
+ dht_length--;
+ dht.clas = bite.hiNibble;
+ dht.tableId = bite.loNibble;
+
+ ubyte[16] dht_code_lengths;
+ istrm.readBytes(dht_code_lengths.ptr, 16);
+ dht_length -= 16;
+
+ version(JPEGDebug)
+ {
+ writefln("DHT class: %s (%s)",
+ dht.clas,
+ dht.clas? "AC":"DC");
+ writefln("DHT tableId: %s", dht.tableId);
+ writefln("DHT Huffman code lengths: %s", dht_code_lengths);
+ }
+
+ // Read Huffman table
+ int totalCodes = reduce!("a + b")(0, dht_code_lengths);
+ int storedCodes = 0;
+ ubyte treeLevel = 0;
+ ushort bits = 0;
+
+ while (storedCodes != totalCodes)
+ {
+ while (treeLevel < 15 &&
+ dht_code_lengths[treeLevel] == 0)
+ {
+ treeLevel++;
+ bits *= 2;
+ }
+
+ if (treeLevel < 16)
+ {
+ uint bitsNum = treeLevel + 1;
+ HuffmanCode code = HuffmanCode(bits, cast(ushort)bitsNum);
+
+ auto entry = HuffmanTableEntry(code, istrm.readNumeric!ubyte);
+ dht.huffmanTable.append(entry);
+
+ dht_length--;
+
+ storedCodes++;
+ bits++;
+ dht_code_lengths[treeLevel]--;
+ }
+ }
+
+ dht.huffmanTree = treeFromTable(dht.huffmanTable);
+ }
+
+ return compound(true, "");
+}
+
+Compound!(bool, string) readSOS(JPEGImage* jpg, InputStream istrm)
+{
+ ushort sos_length = istrm.readNumeric!ushort(Endian.Big);
+ jpg.sos.componentsNum = istrm.readNumeric!ubyte;
+
+ version(JPEGDebug)
+ {
+ writefln("SOS length: %s", sos_length);
+ writefln("SOS components: %s", jpg.sos.componentsNum);
+ }
+
+ jpg.sos.components = New!(JPEGImage.SOSComponent[])(jpg.sos.componentsNum);
+
+ foreach(ref c; jpg.sos.components)
+ {
+ ubyte c_id = istrm.readNumeric!ubyte;
+ ubyte bite = istrm.readNumeric!ubyte;
+ c.tableIdDC = bite.hiNibble;
+ c.tableIdAC = bite.loNibble;
+ version(JPEGDebug)
+ {
+ writefln("SOS component id: %s", c_id);
+ writefln("SOS component %s DC table id: %s", c_id, c.tableIdDC);
+ writefln("SOS component %s AC table id: %s", c_id, c.tableIdAC);
+ }
+ }
+
+ jpg.sos.spectralSelectionStart = istrm.readNumeric!ubyte;
+ jpg.sos.spectralSelectionEnd = istrm.readNumeric!ubyte;
+ ubyte bite = istrm.readNumeric!ubyte;
+ jpg.sos.successiveApproximationBitHigh = bite.hiNibble;
+ jpg.sos.successiveApproximationBitLow = bite.loNibble;
+
+ version(JPEGDebug)
+ {
+ writefln("SOS spectral selection start: %s", jpg.sos.spectralSelectionStart);
+ writefln("SOS spectral selection end: %s", jpg.sos.spectralSelectionEnd);
+ writefln("SOS successive approximation bit: %s", jpg.sos.successiveApproximationBitHigh);
+ writefln("SOS successive approximation bit low: %s", jpg.sos.successiveApproximationBitLow);
+ }
+
+ return compound(true, "");
+}
+
+struct ScanBitStream
+{
+ InputStream istrm;
+
+ bool endMarkerFound = false;
+ uint bytesRead = 0;
+ ubyte prevByte = 0x00;
+ ubyte curByte = 0x00;
+
+ ubyte readNextByte()
+ {
+ ubyte b = istrm.readNumeric!ubyte;
+ bytesRead++;
+ endMarkerFound = (prevByte == 0xFF && b == 0xD9);
+ assert(!endMarkerFound);
+ if (!endMarkerFound)
+ {
+ prevByte = b;
+ curByte = b;
+ return b;
+ }
+ else
+ {
+ curByte = 0;
+ }
+ return curByte;
+ }
+
+ bool readable()
+ {
+ return !istrm.readable || endMarkerFound;
+ }
+
+ uint bitPos = 0;
+
+ // Huffman decode a byte
+ Compound!(bool, string) decodeByte(HuffmanTreeNode* node, ubyte* result)
+ {
+ while(!node.isLeaf)
+ {
+ ubyte b = curByte;
+
+ bool bit = getBit(b, 7-bitPos);
+ bitPos++;
+ if (bitPos == 8)
+ {
+ bitPos = 0;
+ readNextByte();
+
+ if (b == 0xFF)
+ {
+ b = curByte;
+ if (b == 0x00)
+ {
+ readNextByte();
+ }
+ }
+ }
+
+ if (bit)
+ node = node.right;
+ else
+ node = node.left;
+
+ if (node is null)
+ return compound(false, "loadJPEG error: no Huffman code found");
+ }
+
+ *result = node.ch;
+ return compound(true, "");
+ }
+
+ // Read len bits from stream to buffer
+ uint readBits(ubyte len)
+ {
+ uint buffer = 0;
+ uint i = 0;
+ uint by = 0;
+ uint bi = 0;
+
+ while (i < len)
+ {
+ ubyte b = curByte;
+
+ bool bit = getBit(b, 7-bitPos);
+ buffer = setBit(buffer, (by * 8 + bi), bit);
+
+ bi++;
+ if (bi == 8)
+ {
+ bi = 0;
+ by++;
+ }
+
+ i++;
+
+ bitPos++;
+ if (bitPos == 8)
+ {
+ bitPos = 0;
+ readNextByte();
+
+ if (b == 0xFF)
+ {
+ b = curByte;
+ if (b == 0x00)
+ readNextByte();
+ }
+ }
+ }
+
+ return buffer;
+ }
+}
+
+/*
+ * Decodes compressed data and creates RGB image from it
+ */
+Compound!(SuperImage, string) decodeScanData(
+ JPEGImage* jpg,
+ InputStream istrm,
+ SuperImageFactory imgFac)
+{
+ SuperImage img = imgFac.createImage(jpg.sof0.width, jpg.sof0.height, 3, 8);
+
+ MCU mcu;
+ foreach(ci, ref c; jpg.sof0.components)
+ {
+ if (ci == 0)
+ mcu.createYBlocks(c.hSubsampling, c.vSubsampling);
+ else if (ci == 1)
+ mcu.createCbBlocks(c.hSubsampling, c.vSubsampling);
+ else if (ci == 2)
+ mcu.createCrBlocks(c.hSubsampling, c.vSubsampling);
+ }
+
+ Compound!(SuperImage, string) error(string errorMsg)
+ {
+ mcu.free();
+ if (img)
+ {
+ img.free();
+ img = null;
+ }
+ return compound(img, errorMsg);
+ }
+
+ // Decode DCT coefficient from bit buffer
+ int decodeCoef(uint buffer, ubyte numBits)
+ {
+ bool positive = getBit(buffer, 0);
+
+ int value = 0;
+ foreach(j; 0..numBits)
+ {
+ bool bit = getBit(buffer, numBits-1-j);
+ value = setBit(value, j, bit);
+ }
+
+ if (positive)
+ return value;
+ else
+ return value - 2^^numBits + 1;
+ }
+
+ static const ubyte[64] dezigzag =
+ [
+ 0, 1, 8, 16, 9, 2, 3, 10,
+ 17, 24, 32, 25, 18, 11, 4, 5,
+ 12, 19, 26, 33, 40, 48, 41, 34,
+ 27, 20, 13, 6, 7, 14, 21, 28,
+ 35, 42, 49, 56, 57, 50, 43, 36,
+ 29, 22, 15, 23, 30, 37, 44, 51,
+ 58, 59, 52, 45, 38, 31, 39, 46,
+ 53, 60, 61, 54, 47, 55, 62, 63
+ ];
+
+ if (jpg.sos.componentsNum != 3)
+ {
+ return error(format(
+ "loadJPEG error: unsupported number of components: %s",
+ jpg.sos.componentsNum));
+ }
+
+ // Store previous DC coefficients
+ int[3] dcCoefPrev;
+
+ if (jpg.dqt.length == 0)
+ return error("loadJPEG error: no DQTs found");
+
+ ScanBitStream sbs;
+ sbs.endMarkerFound = false;
+ sbs.bytesRead = 0;
+ sbs.prevByte = 0x00;
+ sbs.curByte = 0x00;
+ sbs.istrm = istrm;
+ sbs.readNextByte();
+
+ uint numMCUsH = jpg.sof0.width / mcu.width + ((jpg.sof0.width % mcu.width) > 0);
+ uint numMCUsV = jpg.sof0.height / mcu.height + ((jpg.sof0.height % mcu.height) > 0);
+
+ // Read MCUs
+ foreach(mcuY; 0..numMCUsV)
+ foreach(mcuX; 0..numMCUsH)
+ {
+ // Read MCU for each channel
+ foreach(ci, ref c; jpg.sos.components)
+ {
+ auto tableDC = jpg.getHuffmanTable(0, c.tableIdDC);
+ auto tableAC = jpg.getHuffmanTable(1, c.tableIdAC);
+
+ if (tableDC is null)
+ return error("loadJPEG error: illegal DC table index in MCU component");
+ if (tableAC is null)
+ return error("loadJPEG error: illegal AC table index in MCU component");
+
+ auto component = jpg.sof0.components[ci];
+ auto hblocks = component.hSubsampling;
+ auto vblocks = component.vSubsampling;
+ auto dqtTableId = component.dqtTableId;
+ if (dqtTableId >= jpg.dqt.length)
+ return error("loadJPEG error: illegal DQT table index in MCU component");
+
+ // Read 8x8 blocks
+ foreach(by; 0..vblocks)
+ foreach(bx; 0..hblocks)
+ {
+ int[8*8] block;
+
+ // Read DC coefficient
+ ubyte dcDiffLen;
+ auto res = sbs.decodeByte(tableDC.huffmanTree, &dcDiffLen);
+ if (!res[0]) return error(res[1]);
+
+ if (dcDiffLen > 0)
+ {
+ uint dcBuffer = sbs.readBits(dcDiffLen);
+ dcCoefPrev[ci] += decodeCoef(dcBuffer, dcDiffLen);
+ }
+
+ block[0] = dcCoefPrev[ci];
+
+ // Read AC coefficients
+ {
+ uint i = 1;
+ bool eob = false;
+ while (!eob && i < 64)
+ {
+ ubyte code;
+ res = sbs.decodeByte(tableAC.huffmanTree, &code);
+ if (!res[0]) return error(res[1]);
+
+ if (code == 0x00) // EOB, all next values are zero
+ eob = true;
+ else if (code == 0xF0) // ZRL, next 16 values are zero
+ {
+ foreach(j; 0..16)
+ if (i < 64)
+ {
+ block[i] = 0;
+ i++;
+ }
+ }
+ else
+ {
+ ubyte hi = hiNibble(code);
+ ubyte lo = loNibble(code);
+
+ uint zeroes = hi;
+ foreach(j; 0..zeroes)
+ if (i < 64)
+ {
+ block[i] = 0;
+ i++;
+ }
+
+ int acCoef = 0;
+ if (lo > 0)
+ {
+ uint acBuffer = sbs.readBits(lo);
+ acCoef = decodeCoef(acBuffer, lo);
+ }
+
+ if (i < 64)
+ block[i] = acCoef;
+
+ i++;
+ }
+ }
+ }
+
+ // Multiply block by quantization matrix
+ foreach(i, ref v; block)
+ v *= jpg.dqt[dqtTableId].table[i];
+
+ // Convert matrix from zig-zag order to normal order
+ int[8*8] dctMatrix;
+
+ foreach(i, v; block)
+ dctMatrix[dezigzag[i]] = v;
+
+ idct64(dctMatrix.ptr);
+
+ // Copy the matrix into corresponding channel
+ int* outMatrixPtr;
+ if (ci == 0)
+ outMatrixPtr = mcu.yBlocks[by * hblocks + bx].ptr;
+ else if (ci == 1)
+ outMatrixPtr = mcu.cbBlocks[by * hblocks + bx].ptr;
+ else if (ci == 2)
+ outMatrixPtr = mcu.crBlocks[by * hblocks + bx].ptr;
+ else
+ return error("loadJPEG error: illegal component index");
+
+ for(uint i = 0; i < 64; i++)
+ outMatrixPtr[i] = dctMatrix[i];
+ }
+ }
+
+ // Convert MCU from YCbCr to RGB
+ foreach(y; 0..mcu.height) // Pixel coordinates in MCU
+ foreach(x; 0..mcu.width)
+ {
+ //Color4f col = mcu.getPixel(x, y);
+ uint col = mcu.getPixel(x, y);
+
+ // Pixel coordinates in image
+ uint ix = mcuX * mcu.width + x;
+ uint iy = mcuY * mcu.height + y;
+
+ if (ix < img.width && iy < img.height)
+ img[ix, iy] = col;
+ }
+ }
+
+ version(JPEGDebug)
+ {
+ writefln("Bytes read: %s", sbs.bytesRead);
+ }
+
+ mcu.free();
+
+ return compound(img, "");
+}
+
+/*
+ * MCU struct keeps a storage for one Minimal Code Unit
+ * and provides a generalized interface for decoding
+ * images with different subsampling modes.
+ * Decoder should read 8x8 blocks one by one for each channel
+ * and fill corresponding arrays in MCU.
+ */
+struct MCU
+{
+ uint width;
+ uint height;
+
+ alias int[8*8] Block;
+ Block[] yBlocks;
+ Block[] cbBlocks;
+ Block[] crBlocks;
+
+ uint ySamplesH, ySamplesV;
+ uint cbSamplesH, cbSamplesV;
+ uint crSamplesH, crSamplesV;
+
+ uint yWidth, yHeight;
+ uint cbWidth, cbHeight;
+ uint crWidth, crHeight;
+
+ void createYBlocks(uint hsubsampling, uint vsubsampling)
+ {
+ yBlocks = New!(Block[])(hsubsampling * vsubsampling);
+
+ width = hsubsampling * 8;
+ height = vsubsampling * 8;
+
+ ySamplesH = hsubsampling;
+ ySamplesV = vsubsampling;
+
+ yWidth = width / ySamplesH;
+ yHeight = height / ySamplesV;
+ }
+
+ void createCbBlocks(uint hsubsampling, uint vsubsampling)
+ {
+ cbBlocks = New!(Block[])(hsubsampling * vsubsampling);
+
+ cbSamplesH = hsubsampling;
+ cbSamplesV = vsubsampling;
+
+ cbWidth = width / cbSamplesH;
+ cbHeight = height / cbSamplesV;
+ }
+
+ void createCrBlocks(uint hsubsampling, uint vsubsampling)
+ {
+ crBlocks = New!(Block[])(hsubsampling * vsubsampling);
+
+ crSamplesH = hsubsampling;
+ crSamplesV = vsubsampling;
+
+ crWidth = width / crSamplesH;
+ crHeight = height / crSamplesV;
+ }
+
+ void free()
+ {
+ if (yBlocks.length) Delete(yBlocks);
+ if (cbBlocks.length) Delete(cbBlocks);
+ if (crBlocks.length) Delete(crBlocks);
+ }
+
+ uint getPixel(uint x, uint y) // coordinates relative to upper-left MCU corner
+ {
+ // Y block coordinates
+ uint ybx = x / yWidth;
+ uint yby = y / yHeight;
+ uint ybi = yby * ySamplesH + ybx;
+
+ // Pixel coordinates in Y block
+ uint ybpx = x - ybx * yWidth;
+ uint ybpy = y - yby * yHeight;
+
+ // Cb block coordinates
+ uint cbx = x / cbWidth;
+ uint cby = y / cbHeight;
+ uint cbi = cby * cbSamplesH + cbx;
+
+ // Pixel coordinates in Cb block
+ uint cbpx = (x - cbx * cbWidth) / ySamplesH;
+ uint cbpy = (y - cby * cbHeight) / ySamplesV;
+
+ // Cr block coordinates
+ uint crx = x / crWidth;
+ uint cry = y / crHeight;
+ uint cri = cry * crSamplesH + crx;
+
+ // Pixel coordinates in Cr block
+ uint crpx = (x - crx * crWidth) / ySamplesH;
+ uint crpy = (y - cry * crHeight) / ySamplesV;
+
+ // Get color components
+ float Y = cast(float)yBlocks [ybi][ybpy * 8 + ybpx] + 128.0f;
+ float Cb = cast(float)cbBlocks[cbi][cbpy * 8 + cbpx];
+ float Cr = cast(float)crBlocks[cri][crpy * 8 + crpx];
+
+ // Convert from YCbCr to RGB
+ //Color4f col;
+ uint col_r = clamp(Y + 1.402f * Cr);
+ uint col_g = clamp(Y - 0.34414f * Cb - 0.71414f * Cr);
+ uint col_b = clamp(Y + 1.772f * Cb);
+ //col = col / 255.0f;
+ //col.a = 1.0f;
+
+ return 0xFF000000 | (col_r << 16) | (col_g << 8) | (col_b) ;
+ }
+}
+
+uint clamp(float v) {
+ import std.conv;
+ if (v < 0)
+ return 0;
+ uint res = to!uint(v);
+ if (v > 255)
+ return 255;
+ return res;
+}
diff --git a/3rdparty/dimage/memory.d b/3rdparty/dimage/memory.d
new file mode 100644
index 00000000..0ca3eda6
--- /dev/null
+++ b/3rdparty/dimage/memory.d
@@ -0,0 +1,208 @@
+/*
+Copyright (c) 2015 Timur Gafarov
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.memory;//dlib.core.memory
+
+import std.stdio;
+import std.conv;
+import std.traits;
+import core.stdc.stdlib;
+import core.exception: onOutOfMemoryError;
+
+/*
+ * Tools for manual memory management
+ */
+
+//version = MemoryDebug;
+
+private static size_t _allocatedMemory = 0;
+
+version(MemoryDebug)
+{
+ import std.datetime;
+ import std.algorithm;
+
+ struct AllocationRecord
+ {
+ string type;
+ size_t size;
+ ulong id;
+ bool deleted;
+ }
+
+ AllocationRecord[ulong] records;
+ ulong counter = 0;
+
+ void addRecord(void* p, string type, size_t size)
+ {
+ records[cast(ulong)p] = AllocationRecord(type, size, counter, false);
+ counter++;
+ //writefln("Allocated %s (%s bytes)", type, size);
+ }
+
+ void markDeleted(void* p)
+ {
+ ulong k = cast(ulong)p - psize;
+ //string type = records[k].type;
+ //size_t size = records[k].size;
+ records[k].deleted = true;
+ //writefln("Dellocated %s (%s bytes)", type, size);
+ }
+
+ void printMemoryLog()
+ {
+ writeln("----------------------------------------------------");
+ writeln(" Memory allocation log ");
+ writeln("----------------------------------------------------");
+ auto keys = records.keys;
+ sort!((a, b) => records[a].id < records[b].id)(keys);
+ foreach(k; keys)
+ {
+ AllocationRecord r = records[k];
+ if (r.deleted)
+ writefln(" %s - %s byte(s) at %X", r.type, r.size, k);
+ else
+ writefln("REMAINS: %s - %s byte(s) at %X", r.type, r.size, k);
+ }
+ writeln("----------------------------------------------------");
+ writefln("Total amount of allocated memory: %s", _allocatedMemory);
+ writeln("----------------------------------------------------");
+ }
+}
+else
+{
+ void printMemoryLog() {}
+}
+
+size_t allocatedMemory()
+{
+ return _allocatedMemory;
+}
+
+interface Freeable
+{
+ void free();
+}
+
+enum psize = 8;
+
+T allocate(T, A...) (A args) if (is(T == class))
+{
+ enum size = __traits(classInstanceSize, T);
+ void* p = malloc(size+psize);
+ if (!p)
+ onOutOfMemoryError();
+ auto memory = p[psize..psize+size];
+ *cast(size_t*)p = size;
+ _allocatedMemory += size;
+ version(MemoryDebug)
+ {
+ addRecord(p, T.stringof, size);
+ }
+ auto res = emplace!(T, A)(memory, args);
+ return res;
+}
+
+T* allocate(T, A...) (A args) if (is(T == struct))
+{
+ enum size = T.sizeof;
+ void* p = malloc(size+psize);
+ if (!p)
+ onOutOfMemoryError();
+ auto memory = p[psize..psize+size];
+ *cast(size_t*)p = size;
+ _allocatedMemory += size;
+ version(MemoryDebug)
+ {
+ addRecord(p, T.stringof, size);
+ }
+ return emplace!(T, A)(memory, args);
+}
+
+T allocate(T) (size_t length) if (isArray!T)
+{
+ alias AT = ForeachType!T;
+ size_t size = length * AT.sizeof;
+ auto mem = malloc(size+psize);
+ if (!mem)
+ onOutOfMemoryError();
+ T arr = cast(T)mem[psize..psize+size];
+ foreach(ref v; arr)
+ v = v.init;
+ *cast(size_t*)mem = size;
+ _allocatedMemory += size;
+ version(MemoryDebug)
+ {
+ addRecord(mem, T.stringof, size);
+ }
+ return arr;
+}
+
+void deallocate(T)(ref T obj) if (isArray!T)
+{
+ void* p = cast(void*)obj.ptr;
+ size_t size = *cast(size_t*)(p - psize);
+ free(p - psize);
+ _allocatedMemory -= size;
+ version(MemoryDebug)
+ {
+ markDeleted(p);
+ }
+ obj.length = 0;
+}
+
+void deallocate(T)(T obj) if (is(T == class) || is(T == interface))
+{
+ Object o = cast(Object)obj;
+ void* p = cast(void*)o;
+ size_t size = *cast(size_t*)(p - psize);
+ destroy(obj);
+ free(p - psize);
+ _allocatedMemory -= size;
+ version(MemoryDebug)
+ {
+ markDeleted(p);
+ }
+}
+
+void deallocate(T)(T* obj)
+{
+ void* p = cast(void*)obj;
+ size_t size = *cast(size_t*)(p - psize);
+ destroy(obj);
+ free(p - psize);
+ _allocatedMemory -= size;
+ version(MemoryDebug)
+ {
+ markDeleted(p);
+ }
+}
+
+alias allocate New;
+alias deallocate Delete;
diff --git a/3rdparty/dimage/png.d b/3rdparty/dimage/png.d
new file mode 100644
index 00000000..0b41e10c
--- /dev/null
+++ b/3rdparty/dimage/png.d
@@ -0,0 +1,847 @@
+/*
+Copyright (c) 2011-2015 Timur Gafarov, Martin Cejp
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.png; //dlib.image.io.png
+
+private
+{
+ import std.stdio;
+ import std.math;
+ import std.string;
+ import std.range;
+
+ import dimage.memory;
+ import dimage.stream;
+ import dimage.compound;
+ //import dlib.filesystem.local;
+ //import dlib.math.utils;
+ import dimage.zlib;
+ import dimage.image;
+ //import dlib.image.io.io;
+
+ //import dlib.core.memory;
+ //import dlib.core.stream;
+ //import dlib.core.compound;
+ //import dlib.filesystem.local;
+ //import dlib.math.utils;
+ //import dlib.coding.zlib;
+ //import dlib.image.image;
+ //import dlib.image.io.io;
+}
+
+// uncomment this to see debug messages:
+//version = PNGDebug;
+
+static const ubyte[8] PNGSignature = [137, 80, 78, 71, 13, 10, 26, 10];
+static const ubyte[4] IHDR = ['I', 'H', 'D', 'R'];
+static const ubyte[4] IEND = ['I', 'E', 'N', 'D'];
+static const ubyte[4] IDAT = ['I', 'D', 'A', 'T'];
+static const ubyte[4] PLTE = ['P', 'L', 'T', 'E'];
+static const ubyte[4] tRNS = ['t', 'R', 'N', 'S'];
+static const ubyte[4] bKGD = ['b', 'K', 'G', 'D'];
+static const ubyte[4] tEXt = ['t', 'E', 'X', 't'];
+static const ubyte[4] iTXt = ['i', 'T', 'X', 't'];
+static const ubyte[4] zTXt = ['z', 'T', 'X', 't'];
+
+enum ColorType: ubyte
+{
+ Greyscale = 0, // allowed bit depths: 1, 2, 4, 8 and 16
+ RGB = 2, // allowed bit depths: 8 and 16
+ Palette = 3, // allowed bit depths: 1, 2, 4 and 8
+ GreyscaleAlpha = 4, // allowed bit depths: 8 and 16
+ RGBA = 6, // allowed bit depths: 8 and 16
+ Any = 7 // one of the above
+}
+
+enum FilterMethod: ubyte
+{
+ None = 0,
+ Sub = 1,
+ Up = 2,
+ Average = 3,
+ Paeth = 4
+}
+
+struct PNGChunk
+{
+ uint length;
+ ubyte[4] type;
+ ubyte[] data;
+ uint crc;
+
+ void free()
+ {
+ if (data.ptr)
+ Delete(data);
+ }
+}
+
+struct PNGHeader
+{
+ union
+ {
+ struct
+ {
+ uint width;
+ uint height;
+ ubyte bitDepth;
+ ubyte colorType;
+ ubyte compressionMethod;
+ ubyte filterMethod;
+ ubyte interlaceMethod;
+ };
+ ubyte[13] bytes;
+ }
+}
+class PNGLoadException: ImageLoadException
+{
+ this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
+ {
+ super(msg, file, line, next);
+ }
+}
+
+/*
+ * Load PNG from file using local FileSystem.
+ * Causes GC allocation
+ */
+//SuperImage loadPNG(string filename)
+//{
+// InputStream input = openForInput(filename);
+// auto img = loadPNG(input);
+// input.close();
+// return img;
+//}
+
+/*
+ * Save PNG to file using local FileSystem.
+ * Causes GC allocation
+ */
+//void savePNG(SuperImage img, string filename)
+//{
+// OutputStream output = openForOutput(filename);
+// Compound!(bool, string) res =
+// savePNG(img, output);
+// output.close();
+//
+// if (!res[0])
+// throw new PNGLoadException(res[1]);
+//}
+
+/*
+ * Load PNG from stream using default image factory.
+ * Causes GC allocation
+ */
+SuperImage loadPNG(InputStream istrm)
+{
+ Compound!(SuperImage, string) res =
+ loadPNG(istrm, defaultImageFactory);
+ if (res[0] is null)
+ throw new PNGLoadException(res[1]);
+ else
+ return res[0];
+}
+
+/*
+ * Load PNG from stream using specified image factory.
+ * GC-free
+ */
+Compound!(SuperImage, string) loadPNG(
+ InputStream istrm,
+ SuperImageFactory imgFac)
+{
+ SuperImage img = null;
+
+ Compound!(SuperImage, string) error(string errorMsg)
+ {
+ if (img)
+ {
+ img.free();
+ img = null;
+ }
+ return compound(img, errorMsg);
+ }
+
+ bool readChunk(PNGChunk* chunk)
+ {
+ if (!istrm.readBE!uint(&chunk.length)
+ || !istrm.fillArray(chunk.type))
+ {
+ return false;
+ }
+
+ version(PNGDebug) writefln("Chunk length = %s", chunk.length);
+ version(PNGDebug) writefln("Chunk type = %s", cast(char[])chunk.type);
+
+ if (chunk.length > 0)
+ {
+ chunk.data = New!(ubyte[])(chunk.length);
+
+ if (!istrm.fillArray(chunk.data))
+ {
+ return false;
+ }
+ }
+
+ version(PNGDebug) writefln("Chunk data.length = %s", chunk.data.length);
+
+ if (!istrm.readBE!uint(&chunk.crc))
+ {
+ return false;
+ }
+
+ // TODO: reimplement CRC check with ranges instead of concatenation
+ uint calculatedCRC = crc32(chain(chunk.type[0..$], chunk.data));
+
+ version(PNGDebug)
+ {
+ writefln("Chunk CRC = %X", chunk.crc);
+ writefln("Calculated CRC = %X", calculatedCRC);
+ writeln("-------------------");
+ }
+
+ if (chunk.crc != calculatedCRC)
+ {
+ return false;
+ }
+
+ return true;
+ }
+
+ bool readHeader(PNGHeader* hdr, PNGChunk* chunk)
+ {
+ hdr.bytes[] = chunk.data[];
+ hdr.width = bigEndian(hdr.width);
+ hdr.height = bigEndian(hdr.height);
+
+ version(PNGDebug)
+ {
+ writefln("width = %s", hdr.width);
+ writefln("height = %s", hdr.height);
+ writefln("bitDepth = %s", hdr.bitDepth);
+ writefln("colorType = %s", hdr.colorType);
+ writefln("compressionMethod = %s", hdr.compressionMethod);
+ writefln("filterMethod = %s", hdr.filterMethod);
+ writefln("interlaceMethod = %s", hdr.interlaceMethod);
+ writeln("----------------");
+ }
+
+ return true;
+ }
+
+ ubyte[8] signatureBuffer;
+
+ if (!istrm.fillArray(signatureBuffer))
+ {
+ return error("loadPNG error: signature check failed");
+ }
+
+ version(PNGDebug)
+ {
+ writeln("----------------");
+ writeln("PNG Signature: ", signatureBuffer);
+ writeln("----------------");
+ }
+
+ PNGHeader hdr;
+
+ ZlibDecoder zlibDecoder;
+
+ ubyte[] palette;
+ ubyte[] transparency;
+ uint paletteSize = 0;
+
+ bool endChunk = false;
+ while (!endChunk && istrm.readable)
+ {
+ PNGChunk chunk;
+ bool res = readChunk(&chunk);
+ if (!res)
+ {
+ chunk.free();
+ return error("loadPNG error: failed to read chunk");
+ }
+ else
+ {
+ if (chunk.type == IEND)
+ {
+ endChunk = true;
+ chunk.free();
+ }
+ else if (chunk.type == IHDR)
+ {
+ if (chunk.data.length < hdr.bytes.length)
+ return error("loadPNG error: illegal header chunk");
+
+ readHeader(&hdr, &chunk);
+ chunk.free();
+
+ bool supportedIndexed =
+ (hdr.colorType == ColorType.Palette) &&
+ (hdr.bitDepth == 1 ||
+ hdr.bitDepth == 2 ||
+ hdr.bitDepth == 4 ||
+ hdr.bitDepth == 8);
+
+ if (hdr.bitDepth != 8 && hdr.bitDepth != 16 && !supportedIndexed)
+ return error("loadPNG error: unsupported bit depth");
+
+ if (hdr.compressionMethod != 0)
+ return error("loadPNG error: unsupported compression method");
+
+ if (hdr.filterMethod != 0)
+ return error("loadPNG error: unsupported filter method");
+
+ if (hdr.interlaceMethod != 0)
+ return error("loadPNG error: interlacing is not supported");
+
+ uint bufferLength = ((hdr.width * hdr.bitDepth + 7) / 8) * hdr.height + hdr.height;
+ ubyte[] buffer = New!(ubyte[])(bufferLength);
+
+ zlibDecoder = ZlibDecoder(buffer);
+
+ version(PNGDebug)
+ {
+ writefln("buffer.length = %s", bufferLength);
+ writeln("----------------");
+ }
+ }
+ else if (chunk.type == IDAT)
+ {
+ zlibDecoder.decode(chunk.data);
+ chunk.free();
+ }
+ else if (chunk.type == PLTE)
+ {
+ palette = chunk.data;
+ }
+ else if (chunk.type == tRNS)
+ {
+ transparency = chunk.data;
+ version(PNGDebug)
+ {
+ writeln("----------------");
+ writefln("transparency.length = %s", transparency.length);
+ writeln("----------------");
+ }
+ }
+ else
+ {
+ chunk.free();
+ }
+ }
+ }
+
+ // finalize decoder
+ version(PNGDebug) writefln("zlibDecoder.hasEnded = %s", zlibDecoder.hasEnded);
+ if (!zlibDecoder.hasEnded)
+ return error("loadPNG error: unexpected end of zlib stream");
+
+ ubyte[] buffer = zlibDecoder.buffer;
+ version(PNGDebug) writefln("buffer.length = %s", buffer.length);
+
+ // create image
+ if (hdr.colorType == ColorType.Greyscale)
+ {
+ if (hdr.bitDepth == 8)
+ img = imgFac.createImage(hdr.width, hdr.height, 1, 8);
+ else if (hdr.bitDepth == 16)
+ img = imgFac.createImage(hdr.width, hdr.height, 1, 16);
+ }
+ else if (hdr.colorType == ColorType.GreyscaleAlpha)
+ {
+ if (hdr.bitDepth == 8)
+ img = imgFac.createImage(hdr.width, hdr.height, 2, 8);
+ else if (hdr.bitDepth == 16)
+ img = imgFac.createImage(hdr.width, hdr.height, 2, 16);
+ }
+ else if (hdr.colorType == ColorType.RGB)
+ {
+ if (hdr.bitDepth == 8)
+ img = imgFac.createImage(hdr.width, hdr.height, 3, 8);
+ else if (hdr.bitDepth == 16)
+ img = imgFac.createImage(hdr.width, hdr.height, 3, 16);
+ }
+ else if (hdr.colorType == ColorType.RGBA)
+ {
+ if (hdr.bitDepth == 8)
+ img = imgFac.createImage(hdr.width, hdr.height, 4, 8);
+ else if (hdr.bitDepth == 16)
+ img = imgFac.createImage(hdr.width, hdr.height, 4, 16);
+ }
+ else if (hdr.colorType == ColorType.Palette)
+ {
+ if (transparency.length > 0)
+ img = imgFac.createImage(hdr.width, hdr.height, 4, 8);
+ else
+ img = imgFac.createImage(hdr.width, hdr.height, 3, 8);
+ }
+ else
+ return error("loadPNG error: unsupported color type");
+
+ version(PNGDebug)
+ {
+ writefln("img.width = %s", img.width);
+ writefln("img.height = %s", img.height);
+ writefln("img.bitDepth = %s", img.bitDepth);
+ writefln("img.channels = %s", img.channels);
+ writeln("----------------");
+ }
+
+ bool indexed = (hdr.colorType == ColorType.Palette);
+
+ // don't close the stream, just release our reference
+ istrm = null;
+
+ // apply filtering to the image data
+ ubyte[] buffer2;
+ string errorMsg;
+ if (!filter(&hdr, img.channels, indexed, buffer, buffer2, errorMsg))
+ {
+ return error(errorMsg);
+ }
+ Delete(buffer);
+ buffer = buffer2;
+
+ // if a palette is used, substitute target colors
+ if (indexed)
+ {
+ if (palette.length == 0)
+ return error("loadPNG error: palette chunk not found");
+
+ ubyte[] pdata = New!(ubyte[])(img.width * img.height * img.channels);
+ if (hdr.bitDepth == 8)
+ {
+ for (int i = 0; i < buffer.length; ++i)
+ {
+ ubyte b = buffer[i];
+ pdata[i * img.channels + 0] = palette[b * 3 + 0];
+ pdata[i * img.channels + 1] = palette[b * 3 + 1];
+ pdata[i * img.channels + 2] = palette[b * 3 + 2];
+ if (transparency.length > 0)
+ pdata[i * img.channels + 3] =
+ b < transparency.length ? transparency[b] : 0;
+ }
+ }
+ else // bit depths 1, 2, 4
+ {
+ int srcindex = 0;
+ int srcshift = 8 - hdr.bitDepth;
+ ubyte mask = cast(ubyte)((1 << hdr.bitDepth) - 1);
+ int sz = img.width * img.height;
+ for (int dstindex = 0; dstindex < sz; dstindex++)
+ {
+ auto b = ((buffer[srcindex] >> srcshift) & mask);
+ //assert(b * 3 + 2 < palette.length);
+ pdata[dstindex * img.channels + 0] = palette[b * 3 + 0];
+ pdata[dstindex * img.channels + 1] = palette[b * 3 + 1];
+ pdata[dstindex * img.channels + 2] = palette[b * 3 + 2];
+
+ if (transparency.length > 0)
+ pdata[dstindex * img.channels + 3] =
+ b < transparency.length ? transparency[b] : 0;
+
+ if (srcshift <= 0)
+ {
+ srcshift = 8 - hdr.bitDepth;
+ srcindex++;
+ }
+ else
+ {
+ srcshift -= hdr.bitDepth;
+ }
+ }
+ }
+
+ Delete(buffer);
+ buffer = pdata;
+
+ Delete(palette);
+
+ if (transparency.length > 0)
+ Delete(transparency);
+ }
+
+ //if (img.data.length != buffer.length)
+ // return error("loadPNG error: uncompressed data length mismatch");
+ //
+ //foreach(i, v; buffer)
+ // img.data[i] = v;
+
+ int bufindex = 0;
+ if (hdr.colorType == ColorType.Greyscale)
+ {
+ if (hdr.bitDepth == 8) {
+ //img = imgFac.createImage(hdr.width, hdr.height, 1, 8);
+ for (int i = 0; i < img.length; i++) {
+ img.data[i] = ((cast(uint)buffer[bufindex])<<16) | ((cast(uint)buffer[bufindex])<<8) | ((cast(uint)buffer[bufindex])<<0) | 0xFF000000;
+ bufindex += 1;
+ }
+ } else if (hdr.bitDepth == 16) {
+ //img = imgFac.createImage(hdr.width, hdr.height, 1, 16);
+ assert(false);
+ }
+ }
+ else if (hdr.colorType == ColorType.GreyscaleAlpha)
+ {
+ if (hdr.bitDepth == 8) {
+ //img = imgFac.createImage(hdr.width, hdr.height, 2, 8);
+ for (int i = 0; i < img.length; i++) {
+ img.data[i] = ((cast(uint)buffer[bufindex])<<16) | ((cast(uint)buffer[bufindex])<<8) | ((cast(uint)buffer[bufindex])<<0) | ((cast(uint)buffer[bufindex + 1])<<24);
+ bufindex += 2;
+ }
+ } else if (hdr.bitDepth == 16) {
+ //img = imgFac.createImage(hdr.width, hdr.height, 2, 16);
+ assert(false);
+ }
+ }
+ else if (hdr.colorType == ColorType.RGB)
+ {
+ if (hdr.bitDepth == 8) {
+ //img = imgFac.createImage(hdr.width, hdr.height, 3, 8);
+ for (int i = 0; i < img.length; i++) {
+ img.data[i] = ((cast(uint)buffer[bufindex])<<16) | ((cast(uint)buffer[bufindex + 1])<<8) | ((cast(uint)buffer[bufindex + 2])<<0) | 0xFF000000;
+ bufindex += 3;
+ }
+ } else if (hdr.bitDepth == 16) {
+ //img = imgFac.createImage(hdr.width, hdr.height, 3, 16);
+ assert(false);
+ }
+ }
+ else if (hdr.colorType == ColorType.RGBA)
+ {
+ if (hdr.bitDepth == 8) {
+ //img = imgFac.createImage(hdr.width, hdr.height, 4, 8);
+ for (int i = 0; i < img.length; i++) {
+ img.data[i] = ((cast(uint)buffer[bufindex])<<16) | ((cast(uint)buffer[bufindex + 1])<<8) | ((cast(uint)buffer[bufindex + 2])<<0) | ((cast(uint)buffer[bufindex + 3])<<24);
+ bufindex += 4;
+ }
+ } else if (hdr.bitDepth == 16) {
+ //img = imgFac.createImage(hdr.width, hdr.height, 4, 16);
+ assert(false);
+ }
+ }
+ else if (hdr.colorType == ColorType.Palette)
+ {
+ if (transparency.length > 0) {
+ for (int i = 0; i < img.length; i++) {
+ img.data[i] = ((cast(uint)buffer[bufindex])<<16) | ((cast(uint)buffer[bufindex + 1])<<8) | ((cast(uint)buffer[bufindex + 2])<<0) | ((cast(uint)buffer[bufindex + 3])<<24);
+ bufindex += 4;
+ }
+ //img = imgFac.createImage(hdr.width, hdr.height, 4, 8);
+ } else {
+ //img = imgFac.createImage(hdr.width, hdr.height, 3, 8);
+ for (int i = 0; i < img.length; i++) {
+ img.data[i] = ((cast(uint)buffer[bufindex])<<16) | ((cast(uint)buffer[bufindex + 1])<<8) | ((cast(uint)buffer[bufindex + 2])<<0) | 0xFF000000;
+ bufindex += 3;
+ }
+ }
+ }
+
+ Delete(buffer);
+
+ return compound(img, "");
+}
+
+version (ENABLE_SAVE_PNG) {
+/*
+ * Save PNG to stream.
+ * GC-free
+ */
+Compound!(bool, string) savePNG(SuperImage img, OutputStream output)
+in
+{
+ assert (img.data.length);
+}
+body
+{
+ Compound!(bool, string) error(string errorMsg)
+ {
+ return compound(false, errorMsg);
+ }
+
+ if (img.bitDepth != 8)
+ return error("savePNG error: only 8-bit images are supported by encoder");
+
+ bool writeChunk(ubyte[4] chunkType, ubyte[] chunkData)
+ {
+ PNGChunk hdrChunk;
+ hdrChunk.length = cast(uint)chunkData.length;
+ hdrChunk.type = chunkType;
+ hdrChunk.data = chunkData;
+ hdrChunk.crc = crc32(chain(chunkType[0..$], hdrChunk.data));
+
+ if (!output.writeBE!uint(hdrChunk.length)
+ || !output.writeArray(hdrChunk.type))
+ return false;
+
+ if (chunkData.length)
+ if (!output.writeArray(hdrChunk.data))
+ return false;
+
+ if (!output.writeBE!uint(hdrChunk.crc))
+ return false;
+
+ return true;
+ }
+
+ bool writeHeader()
+ {
+ PNGHeader hdr;
+ hdr.width = networkByteOrder(img.width);
+ hdr.height = networkByteOrder(img.height);
+ hdr.bitDepth = 8;
+ if (img.channels == 4)
+ hdr.colorType = ColorType.RGBA;
+ else if (img.channels == 3)
+ hdr.colorType = ColorType.RGB;
+ else if (img.channels == 2)
+ hdr.colorType = ColorType.GreyscaleAlpha;
+ else if (img.channels == 1)
+ hdr.colorType = ColorType.Greyscale;
+ hdr.compressionMethod = 0;
+ hdr.filterMethod = 0;
+ hdr.interlaceMethod = 0;
+
+ return writeChunk(IHDR, hdr.bytes);
+ }
+
+ output.writeArray(PNGSignature);
+ if (!writeHeader())
+ return error("savePNG error: write failed (disk full?)");
+
+ //TODO: filtering
+ ubyte[] raw = New!(ubyte[])(img.width * img.height * img.channels + img.height);
+ foreach(y; 0..img.height)
+ {
+ auto rowStart = y * (img.width * img.channels + 1);
+ raw[rowStart] = 0; // No filter
+
+ foreach(x; 0..img.width)
+ {
+ auto dataIndex = (y * img.width + x) * img.channels;
+ auto rawIndex = rowStart + 1 + x * img.channels;
+
+ foreach(ch; 0..img.channels)
+ raw[rawIndex + ch] = img.data[dataIndex + ch];
+ }
+ }
+
+ ubyte[] buffer = New!(ubyte[])(64 * 1024);
+ ZlibBufferedEncoder zlibEncoder = ZlibBufferedEncoder(buffer, raw);
+ while (!zlibEncoder.ended)
+ {
+ auto len = zlibEncoder.encode();
+ if (len > 0)
+ writeChunk(IDAT, zlibEncoder.buffer[0..len]);
+ }
+
+ writeChunk(IEND, []);
+
+ Delete(buffer);
+ Delete(raw);
+
+ return compound(true, "");
+}
+}
+
+/*
+ * performs the paeth PNG filter from pixels values:
+ * a = back
+ * b = up
+ * c = up and back
+ */
+pure ubyte paeth(ubyte a, ubyte b, ubyte c)
+{
+ int p = a + b - c;
+ int pa = abs(p - a);
+ int pb = abs(p - b);
+ int pc = abs(p - c);
+ if (pa <= pb && pa <= pc) return a;
+ else if (pb <= pc) return b;
+ else return c;
+}
+
+bool filter(PNGHeader* hdr,
+ uint channels,
+ bool indexed,
+ ubyte[] ibuffer,
+ out ubyte[] obuffer,
+ out string errorMsg)
+{
+ uint dataSize = cast(uint)ibuffer.length;
+ uint scanlineSize;
+
+ uint calculatedSize;
+ if (indexed)
+ {
+ calculatedSize = hdr.width * hdr.height * hdr.bitDepth / 8 + hdr.height;
+ scanlineSize = hdr.width * hdr.bitDepth / 8 + 1;
+ }
+ else
+ {
+ calculatedSize = hdr.width * hdr.height * channels + hdr.height;
+ scanlineSize = hdr.width * channels + 1;
+ }
+
+ version(PNGDebug)
+ {
+ writefln("[filter] dataSize = %s", dataSize);
+ writefln("[filter] calculatedSize = %s", calculatedSize);
+ }
+
+ if (dataSize != calculatedSize)
+ {
+ errorMsg = "loadPNG error: image size and data mismatch";
+ return false;
+ }
+
+ obuffer = New!(ubyte[])(calculatedSize - hdr.height);
+
+ ubyte pback, pup, pupback, cbyte;
+
+ for (int i = 0; i < hdr.height; ++i)
+ {
+ pback = 0;
+
+ // get the first byte of a scanline
+ ubyte scanFilter = ibuffer[i * scanlineSize];
+
+ if (indexed)
+ {
+ // TODO: support filtering for indexed images
+ if (scanFilter != FilterMethod.None)
+ {
+ errorMsg = "loadPNG error: filtering is not supported for indexed images";
+ return false;
+ }
+
+ for (int j = 1; j < scanlineSize; ++j)
+ {
+ ubyte b = ibuffer[(i * scanlineSize) + j];
+ obuffer[(i * (scanlineSize-1) + j - 1)] = b;
+ }
+ continue;
+ }
+
+ for (int j = 0; j < hdr.width; ++j)
+ {
+ for (int k = 0; k < channels; ++k)
+ {
+ if (i == 0) pup = 0;
+ else pup = obuffer[((i-1) * hdr.width + j) * channels + k]; // (hdr.height-(i-1)-1)
+ if (j == 0) pback = 0;
+ else pback = obuffer[(i * hdr.width + j-1) * channels + k];
+ if (i == 0 || j == 0) pupback = 0;
+ else pupback = obuffer[((i-1) * hdr.width + j - 1) * channels + k];
+
+ // get the current byte from ibuffer
+ cbyte = ibuffer[i * (hdr.width * channels + 1) + j * channels + k + 1];
+
+ // filter, then set the current byte in data
+ switch (scanFilter)
+ {
+ case FilterMethod.None:
+ obuffer[(i * hdr.width + j) * channels + k] = cbyte;
+ break;
+ case FilterMethod.Sub:
+ obuffer[(i * hdr.width + j) * channels + k] = cast(ubyte)(cbyte + pback);
+ break;
+ case FilterMethod.Up:
+ obuffer[(i * hdr.width + j) * channels + k] = cast(ubyte)(cbyte + pup);
+ break;
+ case FilterMethod.Average:
+ obuffer[(i * hdr.width + j) * channels + k] = cast(ubyte)(cbyte + (pback + pup) / 2);
+ break;
+ case FilterMethod.Paeth:
+ obuffer[(i * hdr.width + j) * channels + k] = cast(ubyte)(cbyte + paeth(pback, pup, pupback));
+ break;
+ default:
+ errorMsg = format("loadPNG error: unknown scanline filter (%s)", scanFilter);
+ return false;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+uint crc32(R)(R range, uint inCrc = 0) if (isInputRange!R)
+{
+ uint[256] generateTable()
+ {
+ uint[256] table;
+ uint crc;
+ for (int i = 0; i < 256; i++)
+ {
+ crc = i;
+ for (int j = 0; j < 8; j++)
+ crc = crc & 1 ? (crc >> 1) ^ 0xEDB88320UL : crc >> 1;
+ table[i] = crc;
+ }
+ return table;
+ }
+
+ static const uint[256] table = generateTable();
+
+ uint crc;
+
+ crc = inCrc ^ 0xFFFFFFFF;
+ foreach(v; range)
+ crc = (crc >> 8) ^ table[(crc ^ v) & 0xFF];
+
+ return (crc ^ 0xFFFFFFFF);
+}
+
+unittest
+{
+ import std.base64;
+
+ InputStream png() {
+ string minimal =
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAADklEQVR42mL4z8AAEGAAAwEBAGb9nyQAAAAASUVORK5CYII=";
+
+ ubyte[] bytes = Base64.decode(minimal);
+ return new ArrayStream(bytes, bytes.length);
+ }
+
+ SuperImage img = loadPNG(png());
+
+ assert(img.width == 1);
+ assert(img.height == 1);
+ assert(img.channels == 3);
+ assert(img.pixelSize == 3);
+ assert(img.data == [0xff, 0x00, 0x00]);
+
+ createDir("tests", false);
+ savePNG(img, "tests/minimal.png");
+ loadPNG("tests/minimal.png");
+}
+
diff --git a/3rdparty/dimage/stream.d b/3rdparty/dimage/stream.d
new file mode 100644
index 00000000..9ec5c98c
--- /dev/null
+++ b/3rdparty/dimage/stream.d
@@ -0,0 +1,263 @@
+/*
+Copyright (c) 2014 Martin Cejp
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+//dlib.core.stream
+module dimage.stream;
+
+import std.bitmanip;
+import std.stdint;
+import std.conv;
+
+//import dlib.core.memory;
+
+alias StreamPos = uint64_t;
+alias StreamSize = uint64_t;
+alias StreamOffset = int64_t;
+
+class SeekException : Exception
+{
+ this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null)
+ {
+ super(msg, file, line, next);
+ }
+}
+
+/// Seekable
+interface Seekable
+{
+ // Won't throw on invalid position, may throw on a more serious error.
+
+ StreamPos getPosition() @property;
+ bool setPosition(StreamPos pos);
+ StreamSize size();
+
+ // Throw-on-error wrappers
+
+ final StreamPos position(StreamPos pos)
+ {
+ if (!setPosition(pos))
+ throw new SeekException("Cannot set Seekable position to " ~ pos.to!string);
+
+ return pos;
+ }
+
+ final StreamPos position()
+ {
+ return getPosition();
+ }
+
+ // TODO: Non-throwing version
+ final StreamPos seek(StreamOffset amount)
+ {
+ immutable StreamPos seekTo = getPosition() + amount;
+
+ if (!setPosition(seekTo))
+ throw new SeekException("Cannot set Seekable position to " ~ seekTo.to!string);
+
+ return seekTo;
+ }
+}
+
+/// Stream
+interface Stream : Seekable
+{
+ void close();
+ bool seekable();
+}
+
+interface InputStream : Stream
+{
+ // Won't throw on EOF, may throw on a more serious error.
+
+ bool readable();
+ size_t readBytes(void* buffer, size_t count);
+
+ /// Read array.length elements into an pre-allocated array.
+ /// Returns: true if all elements were read, false otherwise
+ final bool fillArray(T)(T[] array)
+ {
+ immutable size_t len = array.length * T.sizeof;
+ return readBytes(array.ptr, len) == len;
+ }
+
+ /// Read an integer in little-endian encoding
+ final bool readLE(T)(T* value)
+ {
+ ubyte[T.sizeof] buffer;
+
+ if (readBytes(buffer.ptr, buffer.length) != buffer.length)
+ return false;
+
+ *value = littleEndianToNative!T(buffer);
+ return true;
+ }
+
+ /// Read an integer in big-endian encoding
+ final bool readBE(T)(T* value)
+ {
+ ubyte[T.sizeof] buffer;
+
+ if (readBytes(buffer.ptr, buffer.length) != buffer.length)
+ return false;
+
+ *value = bigEndianToNative!T(buffer);
+ return true;
+ }
+}
+
+interface OutputStream : Stream
+{
+ // Won't throw on full disk, may throw on a more serious error.
+
+ void flush();
+ bool writeable();
+ size_t writeBytes(const void* buffer, size_t count);
+
+ /// Write array.length elements from array.
+ /// Returns: true if all elements were written, false otherwise
+ final bool writeArray(T)(const T[] array)
+ {
+ immutable size_t len = array.length * T.sizeof;
+ return writeBytes(array.ptr, len) == len;
+ }
+
+ /// Write a string as zero-terminated
+ /// Returns: true on success, false otherwise
+ final bool writeStringz(string text)
+ {
+ ubyte[1] zero = [0];
+
+ return writeBytes(text.ptr, text.length)
+ && writeBytes(zero.ptr, zero.length);
+ }
+
+ /// Write an integer in little-endian encoding
+ final bool writeLE(T)(const T value)
+ {
+ ubyte[T.sizeof] buffer = nativeToLittleEndian!T(value);
+
+ return writeBytes(buffer.ptr, buffer.length) == buffer.length;
+ }
+
+ /// Write an integer in big-endian encoding
+ final bool writeBE(T)(const T value)
+ {
+ ubyte[T.sizeof] buffer = nativeToBigEndian!T(value);
+
+ return writeBytes(buffer.ptr, buffer.length) == buffer.length;
+ }
+}
+
+interface IOStream : InputStream, OutputStream
+{
+}
+
+StreamSize copyFromTo(InputStream input, OutputStream output)
+{
+ ubyte[0x1000] buffer;
+ StreamSize total = 0;
+
+ while (input.readable)
+ {
+ size_t have = input.readBytes(buffer.ptr, buffer.length);
+
+ if (have == 0)
+ break;
+
+ output.writeBytes(buffer.ptr, have);
+ total += have;
+ }
+
+ return total;
+}
+
+// TODO: Move this?
+// TODO: Add OutputStream methods
+class ArrayStream : InputStream {
+ import std.algorithm;
+
+ this() {
+ }
+
+ this(ubyte[] data, size_t size) {
+ assert(size_ <= data.length);
+
+ this.size_ = size;
+ this.data = data;
+ }
+
+ override void close() {
+ this.pos = 0;
+ this.size_ = 0;
+ this.data = null;
+ }
+
+ override bool readable() {
+ return pos < size_;
+ }
+
+ override size_t readBytes(void* buffer, size_t count) {
+ import std.c.string;
+
+ count = min(count, size_ - pos);
+
+ // whoops, memcpy out of nowhere, can we do better than that?
+ memcpy(buffer, data.ptr + pos, count);
+
+ pos += count;
+ return count;
+ }
+
+ override bool seekable() {
+ return true;
+ }
+
+ override StreamPos getPosition() {
+ return pos;
+ }
+
+ override bool setPosition(StreamPos pos) {
+ if (pos > size_)
+ return false;
+
+ this.pos = cast(size_t)pos;
+ return true;
+ }
+
+ override StreamSize size() {
+ return size;
+ }
+
+ //mixin ManualModeImpl;
+ //mixin FreeImpl;
+
+ private:
+ size_t pos = 0, size_ = 0;
+ ubyte[] data; // data.length is capacity
+}
diff --git a/3rdparty/dimage/zlib.d b/3rdparty/dimage/zlib.d
new file mode 100644
index 00000000..87d07819
--- /dev/null
+++ b/3rdparty/dimage/zlib.d
@@ -0,0 +1,165 @@
+/*
+Copyright (c) 2011-2015 Timur Gafarov
+
+Boost Software License - Version 1.0 - August 17th, 2003
+
+Permission is hereby granted, free of charge, to any person or organization
+obtaining a copy of the software and accompanying documentation covered by
+this license (the "Software") to use, reproduce, display, distribute,
+execute, and transmit the Software, and to prepare derivative works of the
+Software, and to permit third-parties to whom the Software is furnished to
+do so, all subject to the following:
+
+The copyright notices in the Software and this entire statement, including
+the above license grant, this restriction and the following disclaimer,
+must be included in all copies of the Software, in whole or in part, and
+all derivative works of the Software, unless such copies or derivative
+works are solely in the form of machine-executable object code generated by
+a source language processor.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+DEALINGS IN THE SOFTWARE.
+*/
+
+// dimage is actually stripped out part of dlib - just to support reading PNG and JPEG
+module dimage.zlib; //dlib.coding.zlib
+
+private
+{
+ import etc.c.zlib;
+ import dimage.memory;
+}
+
+struct ZlibBufferedEncoder
+{
+ z_stream zlibStream;
+ ubyte[] buffer;
+ ubyte[] input;
+ bool ended = true;
+
+ this(ubyte[] buf, ubyte[] inp)
+ {
+ buffer = buf;
+ input = inp;
+ zlibStream.next_out = buffer.ptr;
+ zlibStream.avail_out = cast(uint)buffer.length;
+ zlibStream.data_type = Z_BINARY;
+ zlibStream.zalloc = null;
+ zlibStream.zfree = null;
+ zlibStream.opaque = null;
+
+ zlibStream.next_in = inp.ptr;
+ zlibStream.avail_in = cast(uint)inp.length;
+
+ deflateInit(&zlibStream, Z_BEST_COMPRESSION);
+ ended = false;
+ }
+
+ size_t encode()
+ {
+ zlibStream.next_out = buffer.ptr;
+ zlibStream.avail_out = cast(uint)buffer.length;
+ zlibStream.total_out = 0;
+
+ while (zlibStream.avail_out > 0)
+ {
+ int msg = deflate(&zlibStream, Z_FINISH);
+
+ if (msg == Z_STREAM_END)
+ {
+ deflateEnd(&zlibStream);
+ ended = true;
+ return zlibStream.total_out;
+ }
+ else if (msg != Z_OK)
+ {
+ deflateEnd(&zlibStream);
+ return 0;
+ }
+ }
+
+ return zlibStream.total_out;
+ }
+}
+
+struct ZlibDecoder
+{
+ z_stream zlibStream;
+ ubyte[] buffer;
+ int msg = 0;
+
+ bool isInitialized = false;
+ bool hasEnded = false;
+
+ this(ubyte[] buf)
+ {
+ buffer = buf;
+ zlibStream.next_out = buffer.ptr;
+ zlibStream.avail_out = cast(uint)buffer.length;
+ zlibStream.data_type = Z_BINARY;
+ }
+
+ bool decode(ubyte[] input)
+ {
+ zlibStream.next_in = input.ptr;
+ zlibStream.avail_in = cast(uint)input.length;
+
+ if (!isInitialized)
+ {
+ isInitialized = true;
+ msg = inflateInit(&zlibStream);
+ if (msg)
+ {
+ inflateEnd(&zlibStream);
+ return false;
+ }
+ }
+
+ while (zlibStream.avail_in)
+ {
+ msg = inflate(&zlibStream, Z_NO_FLUSH);
+
+ if (msg == Z_STREAM_END)
+ {
+ inflateEnd(&zlibStream);
+ hasEnded = true;
+ reallocateBuffer(zlibStream.total_out);
+ return true;
+ }
+ else if (msg != Z_OK)
+ {
+ inflateEnd(&zlibStream);
+ return false;
+ }
+ else if (zlibStream.avail_out == 0)
+ {
+ reallocateBuffer(buffer.length * 2);
+ zlibStream.next_out = &buffer[buffer.length / 2];
+ zlibStream.avail_out = cast(uint)(buffer.length / 2);
+ }
+ }
+
+ return true;
+ }
+
+ void reallocateBuffer(size_t len)
+ {
+ ubyte[] buffer2 = New!(ubyte[])(len);
+ for(uint i = 0; i < buffer2.length; i++)
+ if (i < buffer.length)
+ buffer2[i] = buffer[i];
+ Delete(buffer);
+ buffer = buffer2;
+ }
+
+ void free()
+ {
+ Delete(buffer);
+ }
+}
+
diff --git a/dlangui-msvc.visualdproj b/dlangui-msvc.visualdproj
index bad7d76f..08264025 100644
--- a/dlangui-msvc.visualdproj
+++ b/dlangui-msvc.visualdproj
@@ -449,38 +449,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dub.json b/dub.json
index 91bcea88..5a30c72b 100644
--- a/dub.json
+++ b/dub.json
@@ -45,7 +45,6 @@
"versions-posix": ["USE_SDL"],
"versions-windows": ["Unicode"],
"dependencies": {
- "dlib": "~>0.7.0",
"derelict-gl3": "~>1.0.16",
"derelict-ft": "~>1.0.2",
"gl3n": "~>1.2.0"
@@ -65,8 +64,6 @@
"versions": ["EmbedStandardResources", "ForceLogs"],
"versions-posix": ["USE_SDL", "USE_FREETYPE", "USE_OPENGL"],
"versions-windows": ["Unicode"],
- "dependencies": {
- "dlib": "~>0.7.0",
"gl3n": "~>1.2.0"
},
"dependencies-posix": {
@@ -80,7 +77,6 @@
"versions": ["USE_SDL", "USE_OPENGL", "USE_FREETYPE", "EmbedStandardResources"],
"versions-windows": ["Unicode"],
"dependencies": {
- "dlib": "~>0.7.0",
"derelict-gl3": "~>1.0.16",
"derelict-ft": "~>1.0.2",
"derelict-sdl2": "~>1.9.7",
@@ -100,7 +96,6 @@
"versions": ["USE_X11", "USE_OPENGL", "USE_FREETYPE", "EmbedStandardResources"],
"versions-windows": ["Unicode"],
"dependencies": {
- "dlib": "~>0.7.0",
"derelict-gl3": "~>1.0.16",
"derelict-ft": "~>1.0.2",
"gl3n": "~>1.2.0",
@@ -112,7 +107,6 @@
"versions": ["USE_DSFML", "USE_OPENGL", "USE_FREETYPE", "EmbedStandardResources"],
"versions-windows": ["Unicode"],
"dependencies": {
- "dlib": "~>0.7.0",
"derelict-gl3": "~>1.0.16",
"derelict-ft": "~>1.0.2",
"gl3n": "~>1.2.0",
diff --git a/examples/d3d/d3d-msvc.visualdproj b/examples/d3d/d3d-msvc.visualdproj
index 8c874af7..a21c2b53 100644
--- a/examples/d3d/d3d-msvc.visualdproj
+++ b/examples/d3d/d3d-msvc.visualdproj
@@ -53,7 +53,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -155,7 +155,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -257,7 +257,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -359,7 +359,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
diff --git a/examples/dmledit/dmledit-msvc.visualdproj b/examples/dmledit/dmledit-msvc.visualdproj
index c97328ad..3804751e 100644
--- a/examples/dmledit/dmledit-msvc.visualdproj
+++ b/examples/dmledit/dmledit-msvc.visualdproj
@@ -53,7 +53,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -155,7 +155,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -257,7 +257,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -359,7 +359,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
diff --git a/examples/example1/example1-msvc.visualdproj b/examples/example1/example1-msvc.visualdproj
index a0153dc0..26ccaf70 100644
--- a/examples/example1/example1-msvc.visualdproj
+++ b/examples/example1/example1-msvc.visualdproj
@@ -53,7 +53,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -155,7 +155,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -257,7 +257,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -359,7 +359,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
diff --git a/examples/helloworld/helloworld-msvc.visualdproj b/examples/helloworld/helloworld-msvc.visualdproj
index fcfa59e8..5f053b76 100644
--- a/examples/helloworld/helloworld-msvc.visualdproj
+++ b/examples/helloworld/helloworld-msvc.visualdproj
@@ -53,7 +53,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -155,7 +155,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -257,7 +257,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -359,7 +359,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
diff --git a/examples/spreadsheet/spreadsheet-msvc.visualdproj b/examples/spreadsheet/spreadsheet-msvc.visualdproj
index d5ec4022..fe36a47c 100644
--- a/examples/spreadsheet/spreadsheet-msvc.visualdproj
+++ b/examples/spreadsheet/spreadsheet-msvc.visualdproj
@@ -53,7 +53,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -155,7 +155,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -257,7 +257,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -359,7 +359,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
diff --git a/examples/tetris/tetris-msvc.visualdproj b/examples/tetris/tetris-msvc.visualdproj
index 74ab211f..1c1ad387 100644
--- a/examples/tetris/tetris-msvc.visualdproj
+++ b/examples/tetris/tetris-msvc.visualdproj
@@ -53,7 +53,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -155,7 +155,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -257,7 +257,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
@@ -359,7 +359,7 @@
$(CC) -c
1
$(DMDInstallDir)windows\bin\dmd.exe
- $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source $(SolutionDir)/deps/de_image/source/interfaces $(SolutionDir)/deps/de_image/source/png $(SolutionDir)/deps/dlib
+ $(SolutionDir)/src $(SolutionDir)/3rdparty $(SolutionDir)/deps/DerelictGL3/source $(SolutionDir)/deps/DerelictUtil/source $(SolutionDir)/deps/DerelictFT/source $(SolutionDir)/deps/DerelictSDL2/source
views views/res views/res/i18n views/res/mdpi views/res/hdpi
$(ConfigurationName)
$(OutDir)
diff --git a/src/dlangui/graphics/images.d b/src/dlangui/graphics/images.d
index 13280100..2080eb5b 100644
--- a/src/dlangui/graphics/images.d
+++ b/src/dlangui/graphics/images.d
@@ -23,14 +23,18 @@ module dlangui.graphics.images;
public import dlangui.core.config;
//version = USE_DEIMAGE;
-version = USE_DLIBIMAGE;
+//version = USE_DLIBIMAGE;
+version = USE_DIMAGE;
version (USE_DEIMAGE) {
import devisualization.image;
import devisualization.image.png;
-}
-
-version (USE_DLIBIMAGE) {
+} else version (USE_DIMAGE) {
+ //import dimage.io;
+ import dimage.image;
+ import dimage.png;
+ import dimage.jpeg;
+} else version (USE_DLIBIMAGE) {
import dlib.image.io.io;
import dlib.image.image;
import dlib.image.io.png;
@@ -78,7 +82,7 @@ ColorDrawBuf loadImage(immutable ubyte[] data, string filename) {
version (USE_DEIMAGE) {
try {
- Image image = imageFromFile(filename);
+ Image image = imageFromData(extension(filename)[1 ..$], cast(ubyte[])data); //imageFromFile(filename);
int w = cast(int)image.width;
int h = cast(int)image.height;
ColorDrawBuf buf = new ColorDrawBuf(w, h);
@@ -133,6 +137,34 @@ ColorDrawBuf loadImage(immutable ubyte[] data, string filename) {
Log.e(to!string(e));
return null;
}
+ } else version (USE_DIMAGE) {
+ import std.algorithm;
+ static import dimage.stream;
+ try {
+ SuperImage image = null;
+ dimage.stream.ArrayStream dlibstream = new dimage.stream.ArrayStream(cast(ubyte[])data, data.length);
+ switch(filename.extension)
+ {
+ case ".jpg", ".JPG", ".jpeg":
+ image = dimage.jpeg.loadJPEG(dlibstream);
+ break;
+ case ".png", ".PNG":
+ image = dimage.png.loadPNG(dlibstream);
+ break;
+ default:
+ break;
+ }
+ //SuperImage image = dlib.image.io.io.loadImage(filename);
+ if (!image)
+ return null;
+ ColorDrawBuf buf = importImage(image);
+ destroy(image);
+ return buf;
+ } catch (Exception e) {
+ Log.e("Failed to load image from file ", filename, " using dlib image");
+ Log.e(to!string(e));
+ return null;
+ }
} else {
try {
std.stream.File f = new std.stream.File(filename);
@@ -163,6 +195,22 @@ version (USE_DLIBIMAGE) {
}
}
+version (USE_DIMAGE) {
+ ColorDrawBuf importImage(SuperImage image) {
+ int w = image.width;
+ int h = image.height;
+ ColorDrawBuf buf = new ColorDrawBuf(w, h);
+ for (int y = 0; y < h; y++) {
+ uint * dstLine = buf.scanLine(y);
+ for (int x = 0; x < w; x++) {
+ uint pixel = image[x, y];
+ dstLine[x] = pixel ^ 0xFF000000;
+ }
+ }
+ return buf;
+ }
+}
+
class ImageDecodingException : Exception {
this(string msg) {
super(msg);