Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / ui / gfx / codec / png_codec_unittest.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <algorithm>
6 #include <cmath>
7
8 #include "base/logging.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/libpng/png.h"
11 #include "third_party/skia/include/core/SkBitmap.h"
12 #include "third_party/skia/include/core/SkColorPriv.h"
13 #include "third_party/skia/include/core/SkUnPreMultiply.h"
14 #include "third_party/zlib/zlib.h"
15 #include "ui/gfx/codec/png_codec.h"
16 #include "ui/gfx/size.h"
17 #include "ui/gfx/skia_util.h"
18
19 namespace gfx {
20
21 namespace {
22
23 void MakeRGBImage(int w, int h, std::vector<unsigned char>* data) {
24   data->resize(w * h * 3);
25   for (int y = 0; y < h; y++) {
26     for (int x = 0; x < w; x++) {
27       unsigned char* org_px = &(*data)[(y * w + x) * 3];
28       org_px[0] = x * 3;      // r
29       org_px[1] = x * 3 + 1;  // g
30       org_px[2] = x * 3 + 2;  // b
31     }
32   }
33 }
34
35 // Set use_transparency to write data into the alpha channel, otherwise it will
36 // be filled with 0xff. With the alpha channel stripped, this should yield the
37 // same image as MakeRGBImage above, so the code below can make reference
38 // images for conversion testing.
39 void MakeRGBAImage(int w, int h, bool use_transparency,
40                    std::vector<unsigned char>* data) {
41   data->resize(w * h * 4);
42   for (int y = 0; y < h; y++) {
43     for (int x = 0; x < w; x++) {
44       unsigned char* org_px = &(*data)[(y * w + x) * 4];
45       org_px[0] = x * 3;      // r
46       org_px[1] = x * 3 + 1;  // g
47       org_px[2] = x * 3 + 2;  // b
48       if (use_transparency)
49         org_px[3] = x*3 + 3;  // a
50       else
51         org_px[3] = 0xFF;     // a (opaque)
52     }
53   }
54 }
55
56 // Creates a palette-based image.
57 void MakePaletteImage(int w, int h,
58                       std::vector<unsigned char>* data,
59                       std::vector<png_color>* palette,
60                       std::vector<unsigned char>* trans_chunk = 0) {
61   data->resize(w * h);
62   palette->resize(w);
63   for (int i = 0; i < w; ++i) {
64     png_color& color = (*palette)[i];
65     color.red = i * 3;
66     color.green = color.red + 1;
67     color.blue = color.red + 2;
68   }
69   for (int y = 0; y < h; y++) {
70     for (int x = 0; x < w; x++) {
71       (*data)[y * w + x] = x;  // palette index
72     }
73   }
74   if (trans_chunk) {
75     trans_chunk->resize(palette->size());
76     for (std::size_t i = 0; i < trans_chunk->size(); ++i) {
77       (*trans_chunk)[i] = i % 256;
78     }
79   }
80 }
81
82 // Creates a grayscale image without an alpha channel.
83 void MakeGrayscaleImage(int w, int h,
84                         std::vector<unsigned char>* data) {
85   data->resize(w * h);
86   for (int y = 0; y < h; y++) {
87     for (int x = 0; x < w; x++) {
88       (*data)[y * w + x] = x;  // gray value
89     }
90   }
91 }
92
93 // Creates a grayscale image with an alpha channel.
94 void MakeGrayscaleAlphaImage(int w, int h,
95                              std::vector<unsigned char>* data) {
96   data->resize(w * h * 2);
97   for (int y = 0; y < h; y++) {
98     for (int x = 0; x < w; x++) {
99       unsigned char* px = &(*data)[(y * w + x) * 2];
100       px[0] = x;        // gray value
101       px[1] = x % 256;  // alpha
102     }
103   }
104 }
105
106 // User write function (to be passed to libpng by EncodeImage) which writes
107 // into a buffer instead of to a file.
108 void WriteImageData(png_structp png_ptr,
109                     png_bytep data,
110                     png_size_t length) {
111   std::vector<unsigned char>& v =
112       *static_cast<std::vector<unsigned char>*>(png_get_io_ptr(png_ptr));
113   v.resize(v.size() + length);
114   memcpy(&v[v.size() - length], data, length);
115 }
116
117 // User flush function; goes with WriteImageData, above.
118 void FlushImageData(png_structp /*png_ptr*/) {
119 }
120
121 // Libpng user error function which allows us to print libpng errors using
122 // Chrome's logging facilities instead of stderr.
123 void LogLibPNGError(png_structp png_ptr,
124                     png_const_charp error_msg) {
125   DLOG(ERROR) << "libpng encode error: " << error_msg;
126   longjmp(png_jmpbuf(png_ptr), 1);
127 }
128
129 // Goes with LogLibPNGError, above.
130 void LogLibPNGWarning(png_structp png_ptr,
131                       png_const_charp warning_msg) {
132   DLOG(ERROR) << "libpng encode warning: " << warning_msg;
133 }
134
135 // Color types supported by EncodeImage. Required because neither libpng nor
136 // PNGCodec::Encode supports all of the required values.
137 enum ColorType {
138   COLOR_TYPE_GRAY = PNG_COLOR_TYPE_GRAY,
139   COLOR_TYPE_GRAY_ALPHA = PNG_COLOR_TYPE_GRAY_ALPHA,
140   COLOR_TYPE_PALETTE = PNG_COLOR_TYPE_PALETTE,
141   COLOR_TYPE_RGB = PNG_COLOR_TYPE_RGB,
142   COLOR_TYPE_RGBA = PNG_COLOR_TYPE_RGBA,
143   COLOR_TYPE_BGR,
144   COLOR_TYPE_BGRA
145 };
146
147 // PNG encoder used for testing. Required because PNGCodec::Encode doesn't do
148 // interlaced, palette-based, or grayscale images, but PNGCodec::Decode is
149 // actually asked to decode these types of images by Chrome.
150 bool EncodeImage(const std::vector<unsigned char>& input,
151                  const int width,
152                  const int height,
153                  ColorType output_color_type,
154                  std::vector<unsigned char>* output,
155                  const int interlace_type = PNG_INTERLACE_NONE,
156                  std::vector<png_color>* palette = 0,
157                  std::vector<unsigned char>* palette_alpha = 0) {
158   DCHECK(output);
159
160   int input_rowbytes = 0;
161   int transforms = PNG_TRANSFORM_IDENTITY;
162
163   switch (output_color_type) {
164     case COLOR_TYPE_GRAY:
165       input_rowbytes = width;
166       break;
167     case COLOR_TYPE_GRAY_ALPHA:
168       input_rowbytes = width * 2;
169       break;
170     case COLOR_TYPE_PALETTE:
171       if (!palette)
172         return false;
173       input_rowbytes = width;
174       break;
175     case COLOR_TYPE_RGB:
176       input_rowbytes = width * 3;
177       break;
178     case COLOR_TYPE_RGBA:
179       input_rowbytes = width * 4;
180       break;
181     case COLOR_TYPE_BGR:
182       input_rowbytes = width * 3;
183       output_color_type = static_cast<ColorType>(PNG_COLOR_TYPE_RGB);
184       transforms |= PNG_TRANSFORM_BGR;
185       break;
186     case COLOR_TYPE_BGRA:
187       input_rowbytes = width * 4;
188       output_color_type = static_cast<ColorType>(PNG_COLOR_TYPE_RGBA);
189       transforms |= PNG_TRANSFORM_BGR;
190       break;
191   };
192
193   png_struct* png_ptr =
194       png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
195   if (!png_ptr)
196     return false;
197   png_infop info_ptr = png_create_info_struct(png_ptr);
198   if (!info_ptr) {
199     png_destroy_write_struct(&png_ptr, NULL);
200     return false;
201   }
202
203   std::vector<png_bytep> row_pointers(height);
204   for (int y = 0 ; y < height; ++y) {
205     row_pointers[y] = const_cast<unsigned char*>(&input[y * input_rowbytes]);
206   }
207
208   if (setjmp(png_jmpbuf(png_ptr))) {
209     png_destroy_write_struct(&png_ptr, &info_ptr);
210     return false;
211   }
212
213   png_set_error_fn(png_ptr, NULL, LogLibPNGError, LogLibPNGWarning);
214   png_set_rows(png_ptr, info_ptr, &row_pointers[0]);
215   png_set_write_fn(png_ptr, output, WriteImageData, FlushImageData);
216   png_set_IHDR(png_ptr, info_ptr, width, height, 8, output_color_type,
217                interlace_type, PNG_COMPRESSION_TYPE_DEFAULT,
218                PNG_FILTER_TYPE_DEFAULT);
219   if (output_color_type == COLOR_TYPE_PALETTE) {
220     png_set_PLTE(png_ptr, info_ptr, &palette->front(), palette->size());
221     if (palette_alpha) {
222       unsigned char* alpha_data = &palette_alpha->front();
223       size_t alpha_size = palette_alpha->size();
224       png_set_tRNS(png_ptr, info_ptr, alpha_data, alpha_size, NULL);
225     }
226   }
227
228   png_write_png(png_ptr, info_ptr, transforms, NULL);
229
230   png_destroy_write_struct(&png_ptr, &info_ptr);
231   return true;
232 }
233
234 }  // namespace
235
236 // Returns true if each channel of the given two colors are "close." This is
237 // used for comparing colors where rounding errors may cause off-by-one.
238 bool ColorsClose(uint32_t a, uint32_t b) {
239   return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
240          abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
241          abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2 &&
242          abs(static_cast<int>(SkColorGetA(a) - SkColorGetA(b))) < 2;
243 }
244
245 // Returns true if the RGB components are "close."
246 bool NonAlphaColorsClose(uint32_t a, uint32_t b) {
247   return abs(static_cast<int>(SkColorGetB(a) - SkColorGetB(b))) < 2 &&
248          abs(static_cast<int>(SkColorGetG(a) - SkColorGetG(b))) < 2 &&
249          abs(static_cast<int>(SkColorGetR(a) - SkColorGetR(b))) < 2;
250 }
251
252 // Returns true if the BGRA 32-bit SkColor specified by |a| is equivalent to the
253 // 8-bit Gray color specified by |b|.
254 bool BGRAGrayEqualsA8Gray(uint32_t a, uint8_t b) {
255   return SkColorGetB(a) == b && SkColorGetG(a) ==  b &&
256          SkColorGetR(a) == b && SkColorGetA(a) == 255;
257 }
258
259 void MakeTestBGRASkBitmap(int w, int h, SkBitmap* bmp) {
260   bmp->setConfig(SkBitmap::kARGB_8888_Config, w, h);
261   bmp->allocPixels();
262
263   uint32_t* src_data = bmp->getAddr32(0, 0);
264   for (int i = 0; i < w * h; i++)
265     src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240);
266 }
267
268 void MakeTestA8SkBitmap(int w, int h, SkBitmap* bmp) {
269   bmp->setConfig(SkBitmap::kA8_Config, w, h);
270   bmp->allocPixels();
271
272   uint8_t* src_data = bmp->getAddr8(0, 0);
273   for (int i = 0; i < w * h; i++)
274     src_data[i] = i % 255;
275 }
276
277 TEST(PNGCodec, EncodeDecodeRGB) {
278   const int w = 20, h = 20;
279
280   // create an image with known values
281   std::vector<unsigned char> original;
282   MakeRGBImage(w, h, &original);
283
284   // encode
285   std::vector<unsigned char> encoded;
286   ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
287                                Size(w, h), w * 3, false,
288                                std::vector<PNGCodec::Comment>(),
289                                &encoded));
290
291   // decode, it should have the same size as the original
292   std::vector<unsigned char> decoded;
293   int outw, outh;
294   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
295                                PNGCodec::FORMAT_RGB, &decoded,
296                                &outw, &outh));
297   ASSERT_EQ(w, outw);
298   ASSERT_EQ(h, outh);
299   ASSERT_EQ(original.size(), decoded.size());
300
301   // Images must be equal
302   ASSERT_TRUE(original == decoded);
303 }
304
305 TEST(PNGCodec, EncodeDecodeRGBA) {
306   const int w = 20, h = 20;
307
308   // create an image with known values, a must be opaque because it will be
309   // lost during encoding
310   std::vector<unsigned char> original;
311   MakeRGBAImage(w, h, true, &original);
312
313   // encode
314   std::vector<unsigned char> encoded;
315   ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGBA,
316                                Size(w, h), w * 4, false,
317                                std::vector<PNGCodec::Comment>(),
318                                &encoded));
319
320   // decode, it should have the same size as the original
321   std::vector<unsigned char> decoded;
322   int outw, outh;
323   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
324                                PNGCodec::FORMAT_RGBA, &decoded,
325                                &outw, &outh));
326   ASSERT_EQ(w, outw);
327   ASSERT_EQ(h, outh);
328   ASSERT_EQ(original.size(), decoded.size());
329
330   // Images must be exactly equal
331   ASSERT_TRUE(original == decoded);
332 }
333
334 TEST(PNGCodec, EncodeDecodeBGRA) {
335   const int w = 20, h = 20;
336
337   // Create an image with known values, alpha must be opaque because it will be
338   // lost during encoding.
339   std::vector<unsigned char> original;
340   MakeRGBAImage(w, h, true, &original);
341
342   // Encode.
343   std::vector<unsigned char> encoded;
344   ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_BGRA,
345                                Size(w, h), w * 4, false,
346                                std::vector<PNGCodec::Comment>(),
347                                &encoded));
348
349   // Decode, it should have the same size as the original.
350   std::vector<unsigned char> decoded;
351   int outw, outh;
352   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
353                                PNGCodec::FORMAT_BGRA, &decoded,
354                                &outw, &outh));
355   ASSERT_EQ(w, outw);
356   ASSERT_EQ(h, outh);
357   ASSERT_EQ(original.size(), decoded.size());
358
359   // Images must be exactly equal.
360   ASSERT_TRUE(original == decoded);
361 }
362
363 TEST(PNGCodec, DecodePalette) {
364   const int w = 20, h = 20;
365
366   // create an image with known values
367   std::vector<unsigned char> original;
368   std::vector<png_color> original_palette;
369   std::vector<unsigned char> original_trans_chunk;
370   MakePaletteImage(w, h, &original, &original_palette, &original_trans_chunk);
371
372   // encode
373   std::vector<unsigned char> encoded;
374   ASSERT_TRUE(EncodeImage(original,
375                           w, h,
376                           COLOR_TYPE_PALETTE,
377                           &encoded,
378                           PNG_INTERLACE_NONE,
379                           &original_palette,
380                           &original_trans_chunk));
381
382   // decode
383   std::vector<unsigned char> decoded;
384   int outw, outh;
385   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
386                                PNGCodec::FORMAT_RGBA, &decoded,
387                                &outw, &outh));
388   ASSERT_EQ(w, outw);
389   ASSERT_EQ(h, outh);
390   ASSERT_EQ(decoded.size(), w * h * 4U);
391
392   // Images must be equal
393   for (int y = 0; y < h; ++y) {
394     for (int x = 0; x < w; ++x) {
395       unsigned char palette_pixel = original[y * w + x];
396       png_color& palette_color = original_palette[palette_pixel];
397       int alpha = original_trans_chunk[palette_pixel];
398       unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
399
400       EXPECT_EQ(palette_color.red, rgba_pixel[0]);
401       EXPECT_EQ(palette_color.green, rgba_pixel[1]);
402       EXPECT_EQ(palette_color.blue, rgba_pixel[2]);
403       EXPECT_EQ(alpha, rgba_pixel[3]);
404     }
405   }
406 }
407
408 TEST(PNGCodec, DecodePaletteDiscardAlpha) {
409   const int w = 20, h = 20;
410
411   // create an image with known values
412   std::vector<unsigned char> original;
413   std::vector<png_color> original_palette;
414   std::vector<unsigned char> original_trans_chunk;
415   MakePaletteImage(w, h, &original, &original_palette, &original_trans_chunk);
416
417   // encode
418   std::vector<unsigned char> encoded;
419   ASSERT_TRUE(EncodeImage(original,
420                           w, h,
421                           COLOR_TYPE_PALETTE,
422                           &encoded,
423                           PNG_INTERLACE_NONE,
424                           &original_palette,
425                           &original_trans_chunk));
426
427   // decode
428   std::vector<unsigned char> decoded;
429   int outw, outh;
430   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
431                                PNGCodec::FORMAT_RGB, &decoded,
432                                &outw, &outh));
433   ASSERT_EQ(w, outw);
434   ASSERT_EQ(h, outh);
435   ASSERT_EQ(decoded.size(), w * h * 3U);
436
437   // Images must be equal
438   for (int y = 0; y < h; ++y) {
439     for (int x = 0; x < w; ++x) {
440       unsigned char palette_pixel = original[y * w + x];
441       png_color& palette_color = original_palette[palette_pixel];
442       unsigned char* rgba_pixel = &decoded[(y * w + x) * 3];
443
444       EXPECT_EQ(palette_color.red, rgba_pixel[0]);
445       EXPECT_EQ(palette_color.green, rgba_pixel[1]);
446       EXPECT_EQ(palette_color.blue, rgba_pixel[2]);
447     }
448   }
449 }
450
451 TEST(PNGCodec, DecodeInterlacedPalette) {
452   const int w = 20, h = 20;
453
454   // create an image with known values
455   std::vector<unsigned char> original;
456   std::vector<png_color> original_palette;
457   std::vector<unsigned char> original_trans_chunk;
458   MakePaletteImage(w, h, &original, &original_palette, &original_trans_chunk);
459
460   // encode
461   std::vector<unsigned char> encoded;
462   ASSERT_TRUE(EncodeImage(original,
463                           w, h,
464                           COLOR_TYPE_PALETTE,
465                           &encoded,
466                           PNG_INTERLACE_ADAM7,
467                           &original_palette,
468                           &original_trans_chunk));
469
470   // decode
471   std::vector<unsigned char> decoded;
472   int outw, outh;
473   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
474                                PNGCodec::FORMAT_RGBA, &decoded,
475                                &outw, &outh));
476   ASSERT_EQ(w, outw);
477   ASSERT_EQ(h, outh);
478   ASSERT_EQ(decoded.size(), w * h * 4U);
479
480   // Images must be equal
481   for (int y = 0; y < h; ++y) {
482     for (int x = 0; x < w; ++x) {
483       unsigned char palette_pixel = original[y * w + x];
484       png_color& palette_color = original_palette[palette_pixel];
485       int alpha = original_trans_chunk[palette_pixel];
486       unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
487
488       EXPECT_EQ(palette_color.red, rgba_pixel[0]);
489       EXPECT_EQ(palette_color.green, rgba_pixel[1]);
490       EXPECT_EQ(palette_color.blue, rgba_pixel[2]);
491       EXPECT_EQ(alpha, rgba_pixel[3]);
492     }
493   }
494 }
495
496 TEST(PNGCodec, DecodeGrayscale) {
497   const int w = 20, h = 20;
498
499   // create an image with known values
500   std::vector<unsigned char> original;
501   MakeGrayscaleImage(w, h, &original);
502
503   // encode
504   std::vector<unsigned char> encoded;
505   ASSERT_TRUE(EncodeImage(original, w, h, COLOR_TYPE_GRAY, &encoded));
506
507   // decode
508   std::vector<unsigned char> decoded;
509   int outw, outh;
510   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
511                                PNGCodec::FORMAT_RGB, &decoded,
512                                &outw, &outh));
513   ASSERT_EQ(w, outw);
514   ASSERT_EQ(h, outh);
515   ASSERT_EQ(decoded.size(), original.size() * 3);
516
517   // Images must be equal
518   for (int y = 0; y < h; ++y) {
519     for (int x = 0; x < w; ++x) {
520       unsigned char gray_pixel = original[(y * w + x)];
521       unsigned char* rgba_pixel = &decoded[(y * w + x) * 3];
522       EXPECT_EQ(rgba_pixel[0], gray_pixel);
523       EXPECT_EQ(rgba_pixel[1], gray_pixel);
524       EXPECT_EQ(rgba_pixel[2], gray_pixel);
525     }
526   }
527 }
528
529 TEST(PNGCodec, DecodeGrayscaleWithAlpha) {
530   const int w = 20, h = 20;
531
532   // create an image with known values
533   std::vector<unsigned char> original;
534   MakeGrayscaleAlphaImage(w, h, &original);
535
536   // encode
537   std::vector<unsigned char> encoded;
538   ASSERT_TRUE(EncodeImage(original,
539                           w, h,
540                           COLOR_TYPE_GRAY_ALPHA,
541                           &encoded));
542
543   // decode
544   std::vector<unsigned char> decoded;
545   int outw, outh;
546   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
547                                PNGCodec::FORMAT_RGBA, &decoded,
548                                &outw, &outh));
549   ASSERT_EQ(w, outw);
550   ASSERT_EQ(h, outh);
551   ASSERT_EQ(decoded.size(), original.size() * 2);
552
553   // Images must be equal
554   for (int y = 0; y < h; ++y) {
555     for (int x = 0; x < w; ++x) {
556       unsigned char* gray_pixel = &original[(y * w + x) * 2];
557       unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
558       EXPECT_EQ(rgba_pixel[0], gray_pixel[0]);
559       EXPECT_EQ(rgba_pixel[1], gray_pixel[0]);
560       EXPECT_EQ(rgba_pixel[2], gray_pixel[0]);
561       EXPECT_EQ(rgba_pixel[3], gray_pixel[1]);
562     }
563   }
564 }
565
566 TEST(PNGCodec, DecodeGrayscaleWithAlphaDiscardAlpha) {
567   const int w = 20, h = 20;
568
569   // create an image with known values
570   std::vector<unsigned char> original;
571   MakeGrayscaleAlphaImage(w, h, &original);
572
573   // encode
574   std::vector<unsigned char> encoded;
575   ASSERT_TRUE(EncodeImage(original,
576                           w, h,
577                           COLOR_TYPE_GRAY_ALPHA,
578                           &encoded));
579
580   // decode
581   std::vector<unsigned char> decoded;
582   int outw, outh;
583   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
584                                PNGCodec::FORMAT_RGB, &decoded,
585                                &outw, &outh));
586   ASSERT_EQ(w, outw);
587   ASSERT_EQ(h, outh);
588   ASSERT_EQ(decoded.size(), w * h * 3U);
589
590   // Images must be equal
591   for (int y = 0; y < h; ++y) {
592     for (int x = 0; x < w; ++x) {
593       unsigned char* gray_pixel = &original[(y * w + x) * 2];
594       unsigned char* rgba_pixel = &decoded[(y * w + x) * 3];
595       EXPECT_EQ(rgba_pixel[0], gray_pixel[0]);
596       EXPECT_EQ(rgba_pixel[1], gray_pixel[0]);
597       EXPECT_EQ(rgba_pixel[2], gray_pixel[0]);
598     }
599   }
600 }
601
602 TEST(PNGCodec, DecodeInterlacedGrayscale) {
603   const int w = 20, h = 20;
604
605   // create an image with known values
606   std::vector<unsigned char> original;
607   MakeGrayscaleImage(w, h, &original);
608
609   // encode
610   std::vector<unsigned char> encoded;
611   ASSERT_TRUE(EncodeImage(original,
612                           w, h,
613                           COLOR_TYPE_GRAY,
614                           &encoded,
615                           PNG_INTERLACE_ADAM7));
616
617   // decode
618   std::vector<unsigned char> decoded;
619   int outw, outh;
620   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
621                                PNGCodec::FORMAT_RGBA, &decoded,
622                                &outw, &outh));
623   ASSERT_EQ(w, outw);
624   ASSERT_EQ(h, outh);
625   ASSERT_EQ(decoded.size(), original.size() * 4);
626
627   // Images must be equal
628   for (int y = 0; y < h; ++y) {
629     for (int x = 0; x < w; ++x) {
630       unsigned char gray_pixel = original[(y * w + x)];
631       unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
632       EXPECT_EQ(rgba_pixel[0], gray_pixel);
633       EXPECT_EQ(rgba_pixel[1], gray_pixel);
634       EXPECT_EQ(rgba_pixel[2], gray_pixel);
635       EXPECT_EQ(rgba_pixel[3], 0xFF);
636     }
637   }
638 }
639
640 TEST(PNGCodec, DecodeInterlacedGrayscaleWithAlpha) {
641   const int w = 20, h = 20;
642
643   // create an image with known values
644   std::vector<unsigned char> original;
645   MakeGrayscaleAlphaImage(w, h, &original);
646
647   // encode
648   std::vector<unsigned char> encoded;
649   ASSERT_TRUE(EncodeImage(original,
650                           w, h,
651                           COLOR_TYPE_GRAY_ALPHA,
652                           &encoded,
653                           PNG_INTERLACE_ADAM7));
654
655   // decode
656   std::vector<unsigned char> decoded;
657   int outw, outh;
658   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
659                                PNGCodec::FORMAT_RGBA, &decoded,
660                                &outw, &outh));
661   ASSERT_EQ(w, outw);
662   ASSERT_EQ(h, outh);
663   ASSERT_EQ(decoded.size(), original.size() * 2);
664
665   // Images must be equal
666   for (int y = 0; y < h; ++y) {
667     for (int x = 0; x < w; ++x) {
668       unsigned char* gray_pixel = &original[(y * w + x) * 2];
669       unsigned char* rgba_pixel = &decoded[(y * w + x) * 4];
670       EXPECT_EQ(rgba_pixel[0], gray_pixel[0]);
671       EXPECT_EQ(rgba_pixel[1], gray_pixel[0]);
672       EXPECT_EQ(rgba_pixel[2], gray_pixel[0]);
673       EXPECT_EQ(rgba_pixel[3], gray_pixel[1]);
674     }
675   }
676 }
677
678 TEST(PNGCodec, DecodeInterlacedRGB) {
679   const int w = 20, h = 20;
680
681   // create an image with known values
682   std::vector<unsigned char> original;
683   MakeRGBImage(w, h, &original);
684
685   // encode
686   std::vector<unsigned char> encoded;
687   ASSERT_TRUE(EncodeImage(original,
688                           w, h,
689                           COLOR_TYPE_RGB,
690                           &encoded,
691                           PNG_INTERLACE_ADAM7));
692
693   // decode, it should have the same size as the original
694   std::vector<unsigned char> decoded;
695   int outw, outh;
696   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
697                                PNGCodec::FORMAT_RGB, &decoded,
698                                &outw, &outh));
699   ASSERT_EQ(w, outw);
700   ASSERT_EQ(h, outh);
701   ASSERT_EQ(original.size(), decoded.size());
702
703   // Images must be equal
704   ASSERT_EQ(original, decoded);
705 }
706
707 TEST(PNGCodec, DecodeInterlacedRGBA) {
708   const int w = 20, h = 20;
709
710   // create an image with known values
711   std::vector<unsigned char> original;
712   MakeRGBAImage(w, h, false, &original);
713
714   // encode
715   std::vector<unsigned char> encoded;
716   ASSERT_TRUE(EncodeImage(original,
717                           w, h,
718                           COLOR_TYPE_RGBA,
719                           &encoded,
720                           PNG_INTERLACE_ADAM7));
721
722   // decode, it should have the same size as the original
723   std::vector<unsigned char> decoded;
724   int outw, outh;
725   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
726                                PNGCodec::FORMAT_RGBA, &decoded,
727                                &outw, &outh));
728   ASSERT_EQ(w, outw);
729   ASSERT_EQ(h, outh);
730   ASSERT_EQ(original.size(), decoded.size());
731
732   // Images must be equal
733   ASSERT_EQ(original, decoded);
734 }
735
736 TEST(PNGCodec, DecodeInterlacedRGBADiscardAlpha) {
737   const int w = 20, h = 20;
738
739   // create an image with known values
740   std::vector<unsigned char> original;
741   MakeRGBAImage(w, h, false, &original);
742
743   // encode
744   std::vector<unsigned char> encoded;
745   ASSERT_TRUE(EncodeImage(original,
746                           w, h,
747                           COLOR_TYPE_RGBA,
748                           &encoded,
749                           PNG_INTERLACE_ADAM7));
750
751   // decode
752   std::vector<unsigned char> decoded;
753   int outw, outh;
754   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
755                                PNGCodec::FORMAT_RGB, &decoded,
756                                &outw, &outh));
757   ASSERT_EQ(w, outw);
758   ASSERT_EQ(h, outh);
759   ASSERT_EQ(decoded.size(), w * h * 3U);
760
761   // Images must be equal
762   for (int x = 0; x < w; x++) {
763     for (int y = 0; y < h; y++) {
764       unsigned char* orig_px = &original[(y * w + x) * 4];
765       unsigned char* dec_px = &decoded[(y * w + x) * 3];
766       EXPECT_EQ(dec_px[0], orig_px[0]);
767       EXPECT_EQ(dec_px[1], orig_px[1]);
768       EXPECT_EQ(dec_px[2], orig_px[2]);
769     }
770   }
771 }
772
773 TEST(PNGCodec, DecodeInterlacedBGR) {
774   const int w = 20, h = 20;
775
776   // create an image with known values
777   std::vector<unsigned char> original;
778   MakeRGBImage(w, h, &original);
779
780   // encode
781   std::vector<unsigned char> encoded;
782   ASSERT_TRUE(EncodeImage(original,
783                           w, h,
784                           COLOR_TYPE_BGR,
785                           &encoded,
786                           PNG_INTERLACE_ADAM7));
787
788   // decode, it should have the same size as the original
789   std::vector<unsigned char> decoded;
790   int outw, outh;
791   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
792                                PNGCodec::FORMAT_BGRA, &decoded,
793                                &outw, &outh));
794   ASSERT_EQ(w, outw);
795   ASSERT_EQ(h, outh);
796   ASSERT_EQ(decoded.size(), w * h * 4U);
797
798   // Images must be equal
799   for (int x = 0; x < w; x++) {
800     for (int y = 0; y < h; y++) {
801       unsigned char* orig_px = &original[(y * w + x) * 3];
802       unsigned char* dec_px = &decoded[(y * w + x) * 4];
803       EXPECT_EQ(dec_px[0], orig_px[0]);
804       EXPECT_EQ(dec_px[1], orig_px[1]);
805       EXPECT_EQ(dec_px[2], orig_px[2]);
806     }
807   }
808 }
809
810 TEST(PNGCodec, DecodeInterlacedBGRA) {
811   const int w = 20, h = 20;
812
813   // create an image with known values
814   std::vector<unsigned char> original;
815   MakeRGBAImage(w, h, false, &original);
816
817   // encode
818   std::vector<unsigned char> encoded;
819   ASSERT_TRUE(EncodeImage(original,
820                           w, h,
821                           COLOR_TYPE_BGRA,
822                           &encoded,
823                           PNG_INTERLACE_ADAM7));
824
825   // decode, it should have the same size as the original
826   std::vector<unsigned char> decoded;
827   int outw, outh;
828   ASSERT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
829                                PNGCodec::FORMAT_BGRA, &decoded,
830                                &outw, &outh));
831   ASSERT_EQ(w, outw);
832   ASSERT_EQ(h, outh);
833   ASSERT_EQ(original.size(), decoded.size());
834
835   // Images must be equal
836   ASSERT_EQ(original, decoded);
837 }
838
839 // Not encoding an interlaced PNG from SkBitmap because we don't do it
840 // anywhere, and the ability to do that requires more code changes.
841 TEST(PNGCodec, DecodeInterlacedRGBtoSkBitmap) {
842   const int w = 20, h = 20;
843
844   // create an image with known values
845   std::vector<unsigned char> original;
846   MakeRGBImage(w, h, &original);
847
848   // encode
849   std::vector<unsigned char> encoded;
850   ASSERT_TRUE(EncodeImage(original,
851                           w, h,
852                           COLOR_TYPE_RGB,
853                           &encoded,
854                           PNG_INTERLACE_ADAM7));
855
856   // Decode the encoded string.
857   SkBitmap decoded_bitmap;
858   ASSERT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
859                                &decoded_bitmap));
860
861   for (int x = 0; x < w; x++) {
862     for (int y = 0; y < h; y++) {
863       const unsigned char* original_pixel = &original[(y * w + x) * 3];
864       const uint32_t original_pixel_sk = SkPackARGB32(0xFF,
865                                                       original_pixel[0],
866                                                       original_pixel[1],
867                                                       original_pixel[2]);
868       const uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
869       EXPECT_EQ(original_pixel_sk, decoded_pixel);
870     }
871   }
872 }
873
874 TEST(PNGCodec, DecodeInterlacedRGBAtoSkBitmap) {
875   const int w = 20, h = 20;
876
877   // create an image with known values
878   std::vector<unsigned char> original;
879   MakeRGBAImage(w, h, false, &original);
880
881   // encode
882   std::vector<unsigned char> encoded;
883   ASSERT_TRUE(EncodeImage(original,
884                           w, h,
885                           COLOR_TYPE_RGBA,
886                           &encoded,
887                           PNG_INTERLACE_ADAM7));
888
889   // Decode the encoded string.
890   SkBitmap decoded_bitmap;
891   ASSERT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
892                                &decoded_bitmap));
893
894   for (int x = 0; x < w; x++) {
895     for (int y = 0; y < h; y++) {
896       const unsigned char* original_pixel = &original[(y * w + x) * 4];
897       const uint32_t original_pixel_sk = SkPackARGB32(original_pixel[3],
898                                                       original_pixel[0],
899                                                       original_pixel[1],
900                                                       original_pixel[2]);
901       const uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
902       EXPECT_EQ(original_pixel_sk, decoded_pixel);
903     }
904   }
905 }
906
907 // Test that corrupted data decompression causes failures.
908 TEST(PNGCodec, DecodeCorrupted) {
909   int w = 20, h = 20;
910
911   // Make some random data (an uncompressed image).
912   std::vector<unsigned char> original;
913   MakeRGBImage(w, h, &original);
914
915   // It should fail when given non-JPEG compressed data.
916   std::vector<unsigned char> output;
917   int outw, outh;
918   EXPECT_FALSE(PNGCodec::Decode(&original[0], original.size(),
919                                 PNGCodec::FORMAT_RGB, &output,
920                                 &outw, &outh));
921
922   // Make some compressed data.
923   std::vector<unsigned char> compressed;
924   ASSERT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
925                                Size(w, h), w * 3, false,
926                                std::vector<PNGCodec::Comment>(),
927                                &compressed));
928
929   // Try decompressing a truncated version.
930   EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size() / 2,
931                                 PNGCodec::FORMAT_RGB, &output,
932                                 &outw, &outh));
933
934   // Corrupt it and try decompressing that.
935   for (int i = 10; i < 30; i++)
936     compressed[i] = i;
937   EXPECT_FALSE(PNGCodec::Decode(&compressed[0], compressed.size(),
938                                 PNGCodec::FORMAT_RGB, &output,
939                                 &outw, &outh));
940 }
941
942 TEST(PNGCodec, StripAddAlpha) {
943   const int w = 20, h = 20;
944
945   // These should be the same except one has a 0xff alpha channel.
946   std::vector<unsigned char> original_rgb;
947   MakeRGBImage(w, h, &original_rgb);
948   std::vector<unsigned char> original_rgba;
949   MakeRGBAImage(w, h, false, &original_rgba);
950
951   // Encode RGBA data as RGB.
952   std::vector<unsigned char> encoded;
953   EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0], PNGCodec::FORMAT_RGBA,
954                                Size(w, h), w * 4, true,
955                                std::vector<PNGCodec::Comment>(),
956                                &encoded));
957
958   // Decode the RGB to RGBA.
959   std::vector<unsigned char> decoded;
960   int outw, outh;
961   EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
962                                PNGCodec::FORMAT_RGBA, &decoded,
963                                &outw, &outh));
964
965   // Decoded and reference should be the same (opaque alpha).
966   ASSERT_EQ(w, outw);
967   ASSERT_EQ(h, outh);
968   ASSERT_EQ(original_rgba.size(), decoded.size());
969   ASSERT_EQ(original_rgba, decoded);
970
971   // Encode RGBA to RGBA.
972   EXPECT_TRUE(PNGCodec::Encode(&original_rgba[0], PNGCodec::FORMAT_RGBA,
973                                Size(w, h), w * 4, false,
974                                std::vector<PNGCodec::Comment>(),
975                                &encoded));
976
977   // Decode the RGBA to RGB.
978   EXPECT_TRUE(PNGCodec::Decode(&encoded[0], encoded.size(),
979                                PNGCodec::FORMAT_RGB, &decoded,
980                                &outw, &outh));
981
982   // It should be the same as our non-alpha-channel reference.
983   ASSERT_EQ(w, outw);
984   ASSERT_EQ(h, outh);
985   ASSERT_EQ(original_rgb.size(), decoded.size());
986   ASSERT_EQ(original_rgb, decoded);
987 }
988
989 TEST(PNGCodec, EncodeBGRASkBitmapStridePadded) {
990   const int kWidth = 20;
991   const int kHeight = 20;
992   const int kPaddedWidth = 32;
993   const int kBytesPerPixel = 4;
994   const int kPaddedSize = kPaddedWidth * kHeight;
995   const int kRowBytes = kPaddedWidth * kBytesPerPixel;
996
997   SkBitmap original_bitmap;
998   original_bitmap.setConfig(SkBitmap::kARGB_8888_Config,
999                             kWidth, kHeight, kRowBytes);
1000   original_bitmap.allocPixels();
1001
1002   // Write data over the source bitmap.
1003   // We write on the pad area here too.
1004   // The encoder should ignore the pad area.
1005   uint32_t* src_data = original_bitmap.getAddr32(0, 0);
1006   for (int i = 0; i < kPaddedSize; i++) {
1007     src_data[i] = SkPreMultiplyARGB(i % 255, i % 250, i % 245, i % 240);
1008   }
1009
1010   // Encode the bitmap.
1011   std::vector<unsigned char> encoded;
1012   PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded);
1013
1014   // Decode the encoded string.
1015   SkBitmap decoded_bitmap;
1016   EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
1017                                &decoded_bitmap));
1018
1019   // Compare the original bitmap and the output bitmap. We use ColorsClose
1020   // as SkBitmaps are considered to be pre-multiplied, the unpremultiplication
1021   // (in Encode) and repremultiplication (in Decode) can be lossy.
1022   for (int x = 0; x < kWidth; x++) {
1023     for (int y = 0; y < kHeight; y++) {
1024       uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x];
1025       uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
1026       EXPECT_TRUE(ColorsClose(original_pixel, decoded_pixel));
1027     }
1028   }
1029 }
1030
1031 TEST(PNGCodec, EncodeBGRASkBitmap) {
1032   const int w = 20, h = 20;
1033
1034   SkBitmap original_bitmap;
1035   MakeTestBGRASkBitmap(w, h, &original_bitmap);
1036
1037   // Encode the bitmap.
1038   std::vector<unsigned char> encoded;
1039   PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded);
1040
1041   // Decode the encoded string.
1042   SkBitmap decoded_bitmap;
1043   EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
1044                                &decoded_bitmap));
1045
1046   // Compare the original bitmap and the output bitmap. We use ColorsClose
1047   // as SkBitmaps are considered to be pre-multiplied, the unpremultiplication
1048   // (in Encode) and repremultiplication (in Decode) can be lossy.
1049   for (int x = 0; x < w; x++) {
1050     for (int y = 0; y < h; y++) {
1051       uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x];
1052       uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
1053       EXPECT_TRUE(ColorsClose(original_pixel, decoded_pixel));
1054     }
1055   }
1056 }
1057
1058 TEST(PNGCodec, EncodeA8SkBitmap) {
1059   const int w = 20, h = 20;
1060
1061   SkBitmap original_bitmap;
1062   MakeTestA8SkBitmap(w, h, &original_bitmap);
1063
1064   // Encode the bitmap.
1065   std::vector<unsigned char> encoded;
1066   EXPECT_TRUE(PNGCodec::EncodeA8SkBitmap(original_bitmap, &encoded));
1067
1068   // Decode the encoded string.
1069   SkBitmap decoded_bitmap;
1070   EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
1071                                &decoded_bitmap));
1072
1073   for (int x = 0; x < w; x++) {
1074     for (int y = 0; y < h; y++) {
1075       uint8_t original_pixel = *original_bitmap.getAddr8(x, y);
1076       uint32_t decoded_pixel = *decoded_bitmap.getAddr32(x, y);
1077       EXPECT_TRUE(BGRAGrayEqualsA8Gray(decoded_pixel, original_pixel));
1078     }
1079   }
1080 }
1081
1082 TEST(PNGCodec, EncodeBGRASkBitmapDiscardTransparency) {
1083   const int w = 20, h = 20;
1084
1085   SkBitmap original_bitmap;
1086   MakeTestBGRASkBitmap(w, h, &original_bitmap);
1087
1088   // Encode the bitmap.
1089   std::vector<unsigned char> encoded;
1090   PNGCodec::EncodeBGRASkBitmap(original_bitmap, true, &encoded);
1091
1092   // Decode the encoded string.
1093   SkBitmap decoded_bitmap;
1094   EXPECT_TRUE(PNGCodec::Decode(&encoded.front(), encoded.size(),
1095                                &decoded_bitmap));
1096
1097   // Compare the original bitmap and the output bitmap. We need to
1098   // unpremultiply original_pixel, as the decoded bitmap doesn't have an alpha
1099   // channel.
1100   for (int x = 0; x < w; x++) {
1101     for (int y = 0; y < h; y++) {
1102       uint32_t original_pixel = original_bitmap.getAddr32(0, y)[x];
1103       uint32_t unpremultiplied =
1104           SkUnPreMultiply::PMColorToColor(original_pixel);
1105       uint32_t decoded_pixel = decoded_bitmap.getAddr32(0, y)[x];
1106       uint32_t unpremultiplied_decoded =
1107           SkUnPreMultiply::PMColorToColor(decoded_pixel);
1108
1109       EXPECT_TRUE(NonAlphaColorsClose(unpremultiplied, unpremultiplied_decoded))
1110           << "Original_pixel: ("
1111           << SkColorGetR(unpremultiplied) << ", "
1112           << SkColorGetG(unpremultiplied) << ", "
1113           << SkColorGetB(unpremultiplied) << "), "
1114           << "Decoded pixel: ("
1115           << SkColorGetR(unpremultiplied_decoded) << ", "
1116           << SkColorGetG(unpremultiplied_decoded) << ", "
1117           << SkColorGetB(unpremultiplied_decoded) << ")";
1118     }
1119   }
1120 }
1121
1122 TEST(PNGCodec, EncodeWithComment) {
1123   const int w = 10, h = 10;
1124
1125   std::vector<unsigned char> original;
1126   MakeRGBImage(w, h, &original);
1127
1128   std::vector<unsigned char> encoded;
1129   std::vector<PNGCodec::Comment> comments;
1130   comments.push_back(PNGCodec::Comment("key", "text"));
1131   comments.push_back(PNGCodec::Comment("test", "something"));
1132   comments.push_back(PNGCodec::Comment("have some", "spaces in both"));
1133   EXPECT_TRUE(PNGCodec::Encode(&original[0], PNGCodec::FORMAT_RGB,
1134                                Size(w, h), w * 3, false, comments, &encoded));
1135
1136   // Each chunk is of the form length (4 bytes), chunk type (tEXt), data,
1137   // checksum (4 bytes).  Make sure we find all of them in the encoded
1138   // results.
1139   const unsigned char kExpected1[] =
1140       "\x00\x00\x00\x08tEXtkey\x00text\x9e\xe7\x66\x51";
1141   const unsigned char kExpected2[] =
1142       "\x00\x00\x00\x0etEXttest\x00something\x29\xba\xef\xac";
1143   const unsigned char kExpected3[] =
1144       "\x00\x00\x00\x18tEXthave some\x00spaces in both\x8d\x69\x34\x2d";
1145
1146   EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected1,
1147                         kExpected1 + arraysize(kExpected1)),
1148             encoded.end());
1149   EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected2,
1150                         kExpected2 + arraysize(kExpected2)),
1151             encoded.end());
1152   EXPECT_NE(std::search(encoded.begin(), encoded.end(), kExpected3,
1153                         kExpected3 + arraysize(kExpected3)),
1154             encoded.end());
1155 }
1156
1157 TEST(PNGCodec, EncodeDecodeWithVaryingCompressionLevels) {
1158   const int w = 20, h = 20;
1159
1160   // create an image with known values, a must be opaque because it will be
1161   // lost during encoding
1162   SkBitmap original_bitmap;
1163   MakeTestBGRASkBitmap(w, h, &original_bitmap);
1164
1165   // encode
1166   std::vector<unsigned char> encoded_normal;
1167   EXPECT_TRUE(
1168       PNGCodec::EncodeBGRASkBitmap(original_bitmap, false, &encoded_normal));
1169
1170   std::vector<unsigned char> encoded_fast;
1171   EXPECT_TRUE(
1172       PNGCodec::FastEncodeBGRASkBitmap(original_bitmap, false, &encoded_fast));
1173
1174   // Make sure the different compression settings actually do something; the
1175   // sizes should be different.
1176   EXPECT_NE(encoded_normal.size(), encoded_fast.size());
1177
1178   // decode, they should be identical to the original.
1179   SkBitmap decoded;
1180   EXPECT_TRUE(
1181       PNGCodec::Decode(&encoded_normal[0], encoded_normal.size(), &decoded));
1182   EXPECT_TRUE(BitmapsAreEqual(decoded, original_bitmap));
1183
1184   EXPECT_TRUE(
1185       PNGCodec::Decode(&encoded_fast[0], encoded_fast.size(), &decoded));
1186   EXPECT_TRUE(BitmapsAreEqual(decoded, original_bitmap));
1187 }
1188
1189
1190 }  // namespace gfx