1 module dplug.gui.screencap;
2 
3 import dplug.core.vec;
4 import dplug.core.nogc;
5 import dplug.core.binrange;
6 import dplug.graphics.image;
7 import dplug.window;
8 
9 import core.stdc.stdio;
10 import core.stdc.string;
11 
12 nothrow @nogc:
13 
14 
15 /// Create a 3D voxel file representing the whole UI.
16 /// Export to a .qb file, as used as input by Qubicle, Goxel, and https://drububu.com/miscellaneous/voxelizer/
17 /// Use https://drububu.com/miscellaneous/voxelizer/ if you want to convert to a MagicaVoxel .vox
18 /// Alternatively: https://github.com/mgerhardy/vengi
19 /// Free the result with `free(slice.ptr)`.
20 ubyte[] encodeScreenshotAsQB(ImageRef!RGBA colorMap, 
21                              WindowPixelFormat pf, // input pixel format
22                              ImageRef!L16 depthMap)
23 {
24     int DEPTH = 16;
25     int ADD_DEPTH = 10; // Additional depth voxels, so that the plugin is more tight.
26 
27     int W = colorMap.w;
28     int H = colorMap.h;
29 
30     Vec!ubyte vox = makeVec!ubyte;
31 
32     vox.writeLE!ubyte(1); // .qb version
33     vox.writeLE!ubyte(1);
34     vox.writeLE!ubyte(0);
35     vox.writeLE!ubyte(0);
36     vox.writeLE!uint(0); // RGBA
37     vox.writeLE!uint(0); // left handed
38     vox.writeLE!uint(0); // uncompressed
39     vox.writeLE!uint(0); // alpha is 0 or 255, tells visibility
40     vox.writeLE!uint(1); // one matrice in file
41     vox.writeLE!ubyte(1); // matrix name
42     vox.writeLE!ubyte('0');
43 
44     // read matrix size 
45     vox.writeLE(W);
46     vox.writeLE(DEPTH + ADD_DEPTH);
47     vox.writeLE(H);
48 
49     vox.writeLE(0); 
50     vox.writeLE(0);
51     vox.writeLE(0); // position
52 
53     for (int z = 0; z < H; z++)
54     {
55         // y inverted in .vox vs screen
56         L16[] depthScan = depthMap.scanline(z);  
57 
58         for (int y = 0; y < DEPTH + ADD_DEPTH; y++)
59         {
60             for (int x = 0; x < W; x++)
61             {
62                 L16 depthHere = depthScan[x];
63                 // note: in magickavoxel, increasing depth is NOT towards viewer
64                 int depth = ADD_DEPTH + (DEPTH * depthHere.l) / 65536;
65                 RGBA color = colorMap[x, z];
66                 vox.writeLE!ubyte(color.r);
67                 vox.writeLE!ubyte(color.g);
68                 vox.writeLE!ubyte(color.b);
69                 if (depth >= y)
70                     vox.writeLE!ubyte(255);
71                 else
72                     vox.writeLE!ubyte(0);
73             }
74         }
75     }
76     return vox.releaseData;
77 }
78 
79 /// Create a PNG screenshot of the whole UI.
80 /// Free the result with `free(slice.ptr)`.
81 ubyte[] encodeScreenshotAsPNG(ImageRef!RGBA colorMap, WindowPixelFormat pf)
82 {
83     import gamut;
84     Image source;
85     source.createViewFromImageRef!RGBA(colorMap);
86 
87     // make a clone to own the memory
88     Image image = source.clone();
89 
90     assert(image.type == PixelType.rgba8);
91     assert(image.hasData);
92 
93     static void swapByte(ref ubyte a, ref ubyte b)
94     {
95         ubyte tmp = a;
96         a = b;
97         b = tmp;
98     }
99 
100     final switch(pf)
101     {
102         case WindowPixelFormat.RGBA8: break;
103         case WindowPixelFormat.BGRA8: 
104             for (int y = 0; y < image.height(); ++y)
105             {
106                 ubyte* scan = cast(ubyte*) image.scanptr(y);
107                 for (int x = 0; x < image.width(); ++x)
108                 {
109                     swapByte(scan[4*x + 0], scan[4*x + 2]);
110                 }
111             }
112             break;
113         case WindowPixelFormat.ARGB8:
114             for (int y = 0; y < image.height(); ++y)
115             {
116                 ubyte* scan = cast(ubyte*) image.scanptr(y);
117                 for (int x = 0; x < image.width(); ++x)
118                 {
119                     ubyte a = scan[4*x + 0];
120                     ubyte r = scan[4*x + 1];
121                     ubyte g = scan[4*x + 2];
122                     ubyte b = scan[4*x + 3];
123                     scan[4*x + 0] = r;
124                     scan[4*x + 1] = g;
125                     scan[4*x + 2] = b;
126                     scan[4*x + 3] = a;
127                 }
128             }
129             break;
130     }        
131 
132     ubyte[] png = image.saveToMemory(ImageFormat.PNG);
133     scope(exit) freeEncodedImage(png);
134 
135     // Return a duped slice because of different freeing functions
136     return mallocDup(png);
137 }