1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "gm/gm.h"
9 #include "include/codec/SkEncodedOrigin.h"
10 #include "include/core/SkCanvas.h"
11 #include "include/core/SkFont.h"
12 #include "include/core/SkImage.h"
13 #include "include/core/SkMaskFilter.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkSize.h"
16 #include "include/core/SkString.h"
17 #include "include/core/SkSurface.h"
18 #include "tools/Resources.h"
19 #include "tools/ToolUtils.h"
20 
21 static constexpr int kImgW = 100;
22 static constexpr int kImgH =  80;
23 
24 /**
25   This function was used to create the images used by these test. It saves them as PNGs (so they
26   are lossless). Then the following bash script was used to create the oriented JPGs with
27   imagemagick and exiftool:
28 #!/bin/bash
29 
30 for s in 444 422 420 440 411 410; do
31   for i in {1..8}; do
32     magick convert $i.png -sampling-factor ${s:0:1}:${s:1:1}:${s:2:1} $i\_$s.jpg;
33     exiftool -orientation=$i -n -m -overwrite_original $i\_$s.jpg;
34   done
35 done
36 
37  */
make_images()38 static void make_images() {
39     for (int i = 1; i <= 8; ++i) {
40         SkISize size{kImgW, kImgH};
41         SkEncodedOrigin origin = static_cast<SkEncodedOrigin>(i);
42         // We apply the inverse transformation to the PNG we generate, convert the PNG to a
43         // a JPEG using magick, then modify the JPEG's tag using exiftool (without modifying the
44         // stored JPEG data).
45         if (origin >= kLeftTop_SkEncodedOrigin) {
46             // The last four SkEncodedOrigin values involve 90 degree rotations
47             using std::swap;
48             swap(size.fWidth, size.fHeight);
49         }
50         using std::swap;
51         auto surf = SkSurface::MakeRaster(SkImageInfo::Make(size,
52                                                             kRGBA_8888_SkColorType,
53                                                             kPremul_SkAlphaType));
54         auto* canvas = surf->getCanvas();
55         SkMatrix m = SkEncodedOriginToMatrix(origin, kImgW, kImgH);
56         SkAssertResult(m.invert(&m));
57         canvas->concat(m);
58         canvas->clear(SK_ColorBLACK);
59         SkPaint paint;
60         paint.setColor(SK_ColorRED);
61         SkScalar midX = kImgW / 2.f;
62         SkScalar midY = kImgH / 2.f;
63         SkScalar w = midX - 1;
64         SkScalar h = midY - 1;
65         canvas->drawRect(SkRect::MakeXYWH(1, 1, w, h), paint);
66         paint.setColor(SK_ColorBLUE);
67         canvas->drawRect(SkRect::MakeXYWH(midX, 1, w, h), paint);
68         paint.setColor(SK_ColorGREEN);
69         canvas->drawRect(SkRect::MakeXYWH(1, midY, w, h), paint);
70         paint.setColor(SK_ColorYELLOW);
71         canvas->drawRect(SkRect::MakeXYWH(midX, midY, w, h), paint);
72         SkFont font(ToolUtils::create_portable_typeface(), kImgH / 4.f);
73 
74         SkPaint blurPaint;
75         blurPaint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, .75f));
76         blurPaint.setColor(SK_ColorBLACK);
77         paint.setColor(SK_ColorWHITE);
78 
79         auto drawLabel = [&](const char* string, SkScalar x, SkScalar y) {
80             canvas->save();
81             canvas->translate(1, 1);
82             canvas->drawString(string, x, y, font, blurPaint);
83             canvas->restore();
84             canvas->drawString(string, x, y, font, paint);
85         };
86 
87         auto measure = [&font](const char* text) {
88             SkRect bounds;
89             font.measureText(text, strlen(text), SkTextEncoding::kUTF8, &bounds);
90             return bounds;
91         };
92 
93         static constexpr SkScalar kPad = 3.f;
94         SkRect bounds;
95 
96         bounds = measure("top");
97         drawLabel("top", midX - bounds.centerX(), -bounds.top() + kPad);
98 
99         bounds = measure("bottom");
100         drawLabel("bottom", midX - bounds.centerX(), kImgH - kPad - bounds.bottom());
101 
102         // It looks weird if "left" and "right" and the number at the center aren't vertically
103         // aligned.
104         SkScalar baseY = midY - measure("leftright").centerY();
105         bounds = measure("left");
106         drawLabel("left", kPad - bounds.left(), baseY);
107 
108         bounds = measure("right");
109         drawLabel("right", kImgW - kPad - bounds.right(), baseY);
110 
111         SkString num = SkStringPrintf("%d", i);
112         bounds = measure(num.c_str());
113         drawLabel(num.c_str(), midX - bounds.centerX(), baseY);
114         num.append(".png");
115         SkPixmap pm;
116         surf->makeImageSnapshot()->peekPixels(&pm);
117         ToolUtils::EncodeImageToFile(num.c_str(), pm, SkEncodedImageFormat::kPNG, 100);
118     }
119 }
120 
121 // This gm draws 8 images that are mostly the same when respecting the
122 // EXIF orientation tag. Each one has four quadrants (red, blue, green,
123 // yellow), and labels on the left, top, right and bottom. The only
124 // visual difference is a number in the middle corresponding to the
125 // EXIF tag for that image's jpg file.
draw(SkCanvas * canvas,const char * suffix)126 static void draw(SkCanvas* canvas, const char* suffix) {
127     // Avoid unused function warning.
128     if (0) {
129         make_images();
130     }
131     canvas->save();
132     for (char i = '1'; i <= '8'; i++) {
133         SkString path = SkStringPrintf("images/orientation/%c%s.jpg", i, suffix);
134         auto image = GetResourceAsImage(path.c_str());
135         if (!image) {
136             continue;
137         }
138         canvas->drawImage(image, 0, 0);
139         if ('4' == i) {
140             canvas->restore();
141             canvas->translate(0, image->height());
142         } else {
143             canvas->translate(image->width(), 0);
144         }
145     }
146 }
147 
148 #define MAKE_GM(subsample) DEF_SIMPLE_GM(orientation_##subsample, canvas, 4*kImgW, 2*kImgH) { \
149         draw(canvas, "_" #subsample);                                                         \
150 }
151 
152 MAKE_GM(410)
153 MAKE_GM(411)
154 MAKE_GM(420)
155 MAKE_GM(422)
156 MAKE_GM(440)
157 MAKE_GM(444)
158