1 module dlibwebp.spec;
2 
3 import dlibwebp.api;
4 import dlib.image;
5 import std.exception;
6 import exceptions;
7 
8 /**
9  * Run the tests like this:
10  * dub test --debug=featureTest
11  *
12  */
13 debug (featureTest) {
14     import feature_test;
15     import std.math;
16 
17     private SuperImage createImageWithColour(PixelFormat format)(int w, int h, Color4f c) {
18         SuperImage img = new Image!(format)(w, h);
19         foreach(int x; 0..img.width) {
20             foreach(int y; 0..img.height) {
21                 img[x, y] = c;
22             }
23         }
24         return img;
25     }
26     private void colorTestLossless(PixelFormat format)(in string fn, Color4f c) {
27         {
28             SuperImage redNonTransparent = createImageWithColour!(format)(500, 400, c);
29             redNonTransparent.saveLosslessWEBP(fn);
30         }
31         SuperImage result = loadWEBP(fn);
32         foreach(int x; 0..result.width) {
33             foreach(int y; 0..result.height) {
34                 // WebP supports maximum 8-bit per channel.
35                 Color4 expected = c.convert(8);
36                 Color4 actual = result[x, y].convert(8);
37                 expected.r.shouldEqual(actual.r);
38                 expected.g.shouldEqual(actual.g);
39                 expected.b.shouldEqual(actual.b);
40                 expected.a.shouldEqual(actual.a);
41             }
42         }
43     }
44 
45     private void assertTheSame8bitWithAlpha(SuperImage source, SuperImage result) {
46         foreach(int x; 0..result.width) {
47             foreach(int y; 0..result.height) {
48                 Color4 expected = source[x, y].convert(8);
49                 Color4 actual = result[x, y].convert(8);
50                 expected.r.shouldEqual(actual.r);
51                 expected.g.shouldEqual(actual.g);
52                 expected.b.shouldEqual(actual.b);
53                 expected.a.shouldEqual(actual.a);
54             }
55         }
56     }
57 
58     unittest {
59 
60         import dlibwebp.random;
61 
62         feature("Filesystem errors.", (f) {
63             f.scenario("Invalid filename lossless.", {
64                 auto invalidFileName = "tttest";
65                 for (int i = 0; i < 10000; i++) {
66                     invalidFileName ~= "_";
67                 }
68                 invalidFileName ~= "test:*?.webp";
69 
70                 // Invalid for the most of file systems.
71                 // https://en.wikipedia.org/wiki/Comparison_of_file_systems#Limits
72 
73                 bool thrownIoException = false;
74                 try {
75                     colorTestLossless!(PixelFormat.RGBA8)(
76                         invalidFileName,
77                         Color4f(1f, 0f, 0f, 1f)
78                     );
79                 } catch (IOException e) {
80                     thrownIoException = true;
81                 }
82                 thrownIoException.shouldBeTrue();
83             });
84         });
85 
86         feature("Filesystem i/o RGBA8. Lossless.", (f) {
87             f.scenario("Red 1.0", {
88                 colorTestLossless!(PixelFormat.RGBA8)(
89                     "ll_RGBA8_red.webp",
90                     Color4f(1f, 0f, 0f, 1f)
91                 );
92             });
93             f.scenario("Red 0.5", {
94                 colorTestLossless!(PixelFormat.RGBA8)(
95                     "ll_RGBA8_red_0_5.webp",
96                     Color4f(0.5f, 0f, 0f, 1f)
97                 );
98             });
99             f.scenario("Red 0.01", {
100                 colorTestLossless!(PixelFormat.RGBA8)(
101                     "ll_RGBA8_red_0_01.webp",
102                     Color4f(0.01f, 0f, 0f, 1f)
103                 );
104             });
105             f.scenario("Green 1.0", {
106                 colorTestLossless!(PixelFormat.RGBA8)(
107                     "ll_RGBA8_green.webp",
108                     Color4f(0f, 1f, 0f, 1f)
109                 );
110             });
111             f.scenario("Blue 1.0", {
112                 colorTestLossless!(PixelFormat.RGBA8)(
113                     "ll_RGBA8_blue.webp",
114                     Color4f(0f, 0f, 1f, 1f)
115                 );
116             });
117             f.scenario("Blue 1.0. Opacity 0.8.", {
118                 colorTestLossless!(PixelFormat.RGBA8)(
119                     "ll_RGBA8_blue_alpha_0_8.webp",
120                     Color4f(0f, 0f, 1f, 0.8f)
121                 );
122             });
123             f.scenario("Random.", {
124                 const fn = "ll_RGBA8_random.webp";
125 
126                 SuperImage img = RandomImages.circles!(PixelFormat.RGBA8)(500, 400);
127                 // Alpha pixel.
128                 img[0, 0] = Color4f(
129                     img[0, 0].r,
130                     img[0, 0].g,
131                     img[0, 0].b,
132                     0.8f);
133                 img.saveLosslessWEBP(fn);
134 
135                 SuperImage result = loadWEBP(fn);
136                 abs(result[0, 0].a - 0.8f).shouldBeLessThan(0.02f); // Alpha pixel!
137                 abs(result[1, 0].a - 1.0f).shouldBeLessThan(0.01f);
138                 assertTheSame8bitWithAlpha(img, result);
139             });
140         });
141 
142 
143         feature("Filesystem i/o RGBA16. Lossless.", (f) {
144             f.scenario("Red 1.0", {
145                 colorTestLossless!(PixelFormat.RGBA16)(
146                     "ll_RGBA16_red.webp",
147                     Color4f(1f, 0f, 0f, 1f)
148                 );
149             });
150             f.scenario("Red 0.5", {
151                 colorTestLossless!(PixelFormat.RGBA16)(
152                     "ll_RGBA16_red_0_5.webp",
153                     Color4f(0.5f, 0f, 0f, 1f)
154                 );
155             });
156             f.scenario("Red 0.01", {
157                 colorTestLossless!(PixelFormat.RGBA16)(
158                     "ll_RGBA16_red_0_01.webp",
159                     Color4f(0.01f, 0f, 0f, 1f)
160                 );
161             });
162             f.scenario("Green 1.0", {
163                 colorTestLossless!(PixelFormat.RGBA16)(
164                     "ll_RGBA16_green.webp",
165                     Color4f(0f, 1f, 0f, 1f)
166                 );
167             });
168             f.scenario("Blue 1.0", {
169                 colorTestLossless!(PixelFormat.RGBA16)(
170                     "ll_RGBA16_blue.webp",
171                     Color4f(0f, 0f, 1f, 1f)
172                 );
173             });
174             f.scenario("Blue 1.0. Opacity 0.8.", {
175                 colorTestLossless!(PixelFormat.RGBA16)(
176                     "ll_RGBA16_blue_alpha_0_8.webp",
177                     Color4f(0f, 0f, 1f, 0.8f)
178                 );
179             });
180             f.scenario("Random.", {
181                 const fn = "ll_RGBA16_random.webp";
182 
183                 SuperImage img = RandomImages.circles!(PixelFormat.RGBA16)(500, 400);
184                 // Alpha pixel.
185                 img[0, 0] = Color4f(
186                     img[0, 0].r,
187                     img[0, 0].g,
188                     img[0, 0].b,
189                     0.8f);
190                 img.saveLosslessWEBP(fn);
191 
192                 SuperImage result = loadWEBP(fn);
193                 abs(result[0, 0].a - 0.8f).shouldBeLessThan(0.02f); // Alpha pixel!
194                 abs(result[1, 0].a - 1.0f).shouldBeLessThan(0.01f);
195                 assertTheSame8bitWithAlpha(img, result);
196             });
197         });
198 
199         feature("Filesystem i/o RGB-8. Lossless.", (f) {
200             f.scenario("Red 1.0", {
201                 colorTestLossless!(PixelFormat.RGB8)(
202                     "ll_RGB8_red.webp",
203                     Color4f(1f, 0f, 0f, 1f)
204                 );
205             });
206             f.scenario("Red 0.5", {
207                 colorTestLossless!(PixelFormat.RGB8)(
208                     "ll_RGB8_red_0_5.webp",
209                     Color4f(0.5f, 0f, 0f, 1f)
210                 );
211             });
212             f.scenario("Red 0.01", {
213                 colorTestLossless!(PixelFormat.RGB8)(
214                     "ll_RGB8_red_0_01.webp",
215                     Color4f(0.01f, 0f, 0f, 1f)
216                 );
217             });
218             f.scenario("Green 1.0", {
219                 colorTestLossless!(PixelFormat.RGB8)(
220                     "ll_RGB8_green.webp",
221                     Color4f(0f, 1f, 0f, 1f)
222                 );
223             });
224             f.scenario("Blue 1.0", {
225                 colorTestLossless!(PixelFormat.RGB8)(
226                     "ll_RGB8_blue.webp",
227                     Color4f(0f, 0f, 1f, 1f)
228                 );
229             });
230             f.scenario("Random.", {
231                 const fn = "ll_RGB8_random.webp";
232                 SuperImage source = RandomImages.circles!(PixelFormat.RGB8)(500, 400);
233                 source.saveLosslessWEBP(fn);
234                 SuperImage result = loadWEBP(fn);
235                 assertTheSame8bitWithAlpha(source, result);
236             });
237         });
238 
239 
240         feature("Filesystem i/o RGB-16. Lossless.", (f) {
241             f.scenario("Red 1.0", {
242                 colorTestLossless!(PixelFormat.RGB16)(
243                     "ll_RGB16_red.webp",
244                     Color4f(1f, 0f, 0f, 1f)
245                 );
246             });
247             f.scenario("Red 0.5", {
248                 colorTestLossless!(PixelFormat.RGB16)(
249                     "ll_RGB16_red_0_5.webp",
250                     Color4f(0.5f, 0f, 0f, 1f)
251                 );
252             });
253             f.scenario("Red 0.01", {
254                 colorTestLossless!(PixelFormat.RGB16)(
255                     "ll_RGB16_red_0_01.webp",
256                     Color4f(0.01f, 0f, 0f, 1f)
257                 );
258             });
259             f.scenario("Green 1.0", {
260                 colorTestLossless!(PixelFormat.RGB16)(
261                     "ll_RGB16_green.webp",
262                     Color4f(0f, 1f, 0f, 1f)
263                 );
264             });
265             f.scenario("Blue 1.0", {
266                 colorTestLossless!(PixelFormat.RGB16)(
267                     "ll_RGB16_blue.webp",
268                     Color4f(0f, 0f, 1f, 1f)
269                 );
270             });
271             f.scenario("Random.", {
272                 const fn = "ll_RGB16_random.webp";
273                 SuperImage source = RandomImages.circles!(PixelFormat.RGB16)(500, 400);
274                 source.saveLosslessWEBP(fn);
275                 SuperImage result = loadWEBP(fn);
276                 // WebP supports only 8 bits per channel anyway.
277                 // And alpha channel will equal 1, just like in the source image.
278                 assertTheSame8bitWithAlpha(source, result);
279             });
280         });
281     }
282 }
283 
284 
285 /*
286 unittest {
287     import dlibwebp.random;
288 
289     SuperImage input = RandomImages.circles(500, 400);
290     string filename = "test_simple.webp";
291     saveIt(input, filename);
292 
293     auto inputL8 = convert!(Image!(PixelFormat.L8))(RandomImages.circles(500, 400));
294     saveIt(inputL8, "test_L8.webp");
295 
296     auto inputLA8 = convert!(Image!(PixelFormat.LA8))(RandomImages.circles(500, 400));
297     saveIt(inputLA8, "test_LA8.webp");
298 
299     auto inputRgba16 = convert!(Image!(PixelFormat.RGBA16))(RandomImages.circles(1920, 1080));
300     saveIt(inputRgba16, "test_RGBA16.webp");
301 }
302 
303 */