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