| /* | |
| * Copyright 2018 Google LLC | |
| * | |
| * Use of this source code is governed by a BSD-style license that can be | |
| * found in the LICENSE file. | |
| */ | |
| #include "gm/gm.h" | |
| #include "include/codec/SkEncodedOrigin.h" | |
| #include "include/core/SkBlurTypes.h" | |
| #include "include/core/SkCanvas.h" | |
| #include "include/core/SkFont.h" | |
| #include "include/core/SkImage.h" | |
| #include "include/core/SkMaskFilter.h" | |
| #include "include/core/SkRefCnt.h" | |
| #include "include/core/SkSize.h" | |
| #include "include/core/SkString.h" | |
| #include "include/core/SkSurface.h" | |
| #include "src/codec/SkCodecImageGenerator.h" | |
| #include "tools/DecodeUtils.h" | |
| #include "tools/EncodeUtils.h" | |
| #include "tools/Resources.h" | |
| #include "tools/ToolUtils.h" | |
| #include "tools/fonts/FontToolUtils.h" | |
| static constexpr int kImgW = 100; | |
| static constexpr int kImgH = 80; | |
| /** | |
| This function was used to create the images used by these test. It saves them as PNGs (so they | |
| are lossless). Then the following bash script was used to create the oriented JPGs with | |
| imagemagick and exiftool: | |
| #!/bin/bash | |
| for s in 444 422 420 440 411 410; do | |
| for i in {1..8}; do | |
| magick convert $i.png -sampling-factor ${s:0:1}:${s:1:1}:${s:2:1} $i\_$s.jpg; | |
| exiftool -orientation=$i -n -m -overwrite_original $i\_$s.jpg; | |
| done | |
| done | |
| */ | |
| static void make_images() { | |
| for (int i = 1; i <= 8; ++i) { | |
| SkISize size{kImgW, kImgH}; | |
| SkEncodedOrigin origin = static_cast<SkEncodedOrigin>(i); | |
| // We apply the inverse transformation to the PNG we generate, convert the PNG to a | |
| // a JPEG using magick, then modify the JPEG's tag using exiftool (without modifying the | |
| // stored JPEG data). | |
| if (origin >= kLeftTop_SkEncodedOrigin) { | |
| // The last four SkEncodedOrigin values involve 90 degree rotations | |
| using std::swap; | |
| swap(size.fWidth, size.fHeight); | |
| } | |
| using std::swap; | |
| auto surf = SkSurfaces::Raster( | |
| SkImageInfo::Make(size, kRGBA_8888_SkColorType, kPremul_SkAlphaType)); | |
| auto* canvas = surf->getCanvas(); | |
| SkMatrix m = SkEncodedOriginToMatrixInverse(origin, kImgW, kImgH); | |
| canvas->concat(m); | |
| canvas->clear(SK_ColorBLACK); | |
| SkPaint paint; | |
| paint.setColor(SK_ColorRED); | |
| SkScalar midX = kImgW / 2.f; | |
| SkScalar midY = kImgH / 2.f; | |
| SkScalar w = midX - 1; | |
| SkScalar h = midY - 1; | |
| canvas->drawRect(SkRect::MakeXYWH(1, 1, w, h), paint); | |
| paint.setColor(SK_ColorBLUE); | |
| canvas->drawRect(SkRect::MakeXYWH(midX, 1, w, h), paint); | |
| paint.setColor(SK_ColorGREEN); | |
| canvas->drawRect(SkRect::MakeXYWH(1, midY, w, h), paint); | |
| paint.setColor(SK_ColorYELLOW); | |
| canvas->drawRect(SkRect::MakeXYWH(midX, midY, w, h), paint); | |
| SkFont font(ToolUtils::DefaultPortableTypeface(), kImgH / 4.f); | |
| SkPaint blurPaint; | |
| blurPaint.setMaskFilter(SkMaskFilter::MakeBlur(kNormal_SkBlurStyle, .75f)); | |
| blurPaint.setColor(SK_ColorBLACK); | |
| paint.setColor(SK_ColorWHITE); | |
| auto drawLabel = [&](const char* string, SkScalar x, SkScalar y) { | |
| canvas->save(); | |
| canvas->translate(1, 1); | |
| canvas->drawString(string, x, y, font, blurPaint); | |
| canvas->restore(); | |
| canvas->drawString(string, x, y, font, paint); | |
| }; | |
| auto measure = [&font](const char* text) { | |
| SkRect bounds; | |
| font.measureText(text, strlen(text), SkTextEncoding::kUTF8, &bounds); | |
| return bounds; | |
| }; | |
| static constexpr SkScalar kPad = 3.f; | |
| SkRect bounds; | |
| bounds = measure("top"); | |
| drawLabel("top", midX - bounds.centerX(), -bounds.top() + kPad); | |
| bounds = measure("bottom"); | |
| drawLabel("bottom", midX - bounds.centerX(), kImgH - kPad - bounds.bottom()); | |
| // It looks weird if "left" and "right" and the number at the center aren't vertically | |
| // aligned. | |
| SkScalar baseY = midY - measure("leftright").centerY(); | |
| bounds = measure("left"); | |
| drawLabel("left", kPad - bounds.left(), baseY); | |
| bounds = measure("right"); | |
| drawLabel("right", kImgW - kPad - bounds.right(), baseY); | |
| SkString num = SkStringPrintf("%d", i); | |
| bounds = measure(num.c_str()); | |
| drawLabel(num.c_str(), midX - bounds.centerX(), baseY); | |
| num.append(".png"); | |
| SkPixmap pm; | |
| surf->makeImageSnapshot()->peekPixels(&pm); | |
| ToolUtils::EncodeImageToPngFile(num.c_str(), pm); | |
| } | |
| } | |
| // This gm draws 8 images that are mostly the same when respecting the | |
| // EXIF orientation tag. Each one has four quadrants (red, blue, green, | |
| // yellow), and labels on the left, top, right and bottom. The only | |
| // visual difference is a number in the middle corresponding to the | |
| // EXIF tag for that image's jpg file. | |
| static void draw(SkCanvas* canvas, const char* suffix) { | |
| // Avoid unused function warning. | |
| if ((false)) { | |
| make_images(); | |
| } | |
| canvas->save(); | |
| for (char i = '1'; i <= '8'; i++) { | |
| SkString path = SkStringPrintf("images/orientation/%c%s.jpg", i, suffix); | |
| auto image = ToolUtils::GetResourceAsImage(path.c_str()); | |
| if (!image) { | |
| continue; | |
| } | |
| canvas->drawImage(image, 0, 0); | |
| if ('4' == i) { | |
| canvas->restore(); | |
| canvas->translate(0, image->height()); | |
| } else { | |
| canvas->translate(image->width(), 0); | |
| } | |
| } | |
| } | |
| #define MAKE_GM(subsample) DEF_SIMPLE_GM(orientation_##subsample, canvas, 4*kImgW, 2*kImgH) { \ | |
| draw(canvas, "_" #subsample); \ | |
| } | |
| MAKE_GM(410) | |
| MAKE_GM(411) | |
| MAKE_GM(420) | |
| MAKE_GM(422) | |
| MAKE_GM(440) | |
| MAKE_GM(444) | |
| // This GM demonstrates that the default SkImageGenerator respects the orientation flag. | |
| DEF_SIMPLE_GM(respect_orientation_jpeg, canvas, 4*kImgW, 2*kImgH) { | |
| canvas->save(); | |
| for (char i = '1'; i <= '8'; i++) { | |
| SkString path = SkStringPrintf("images/orientation/%c_444.jpg", i); | |
| // Get the image as data | |
| sk_sp<SkData> data(GetResourceAsData(path.c_str())); | |
| sk_sp<SkImage> image = SkImages::DeferredFromGenerator( | |
| SkCodecImageGenerator::MakeFromEncodedCodec(data)); | |
| if (!image) { | |
| continue; | |
| } | |
| canvas->drawImage(image, 0, 0); | |
| if ('4' == i || '8' == i) { | |
| canvas->restore(); | |
| canvas->translate(0, image->height()); | |
| } else { | |
| canvas->translate(image->width(), 0); | |
| } | |
| } | |
| } |