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