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