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