aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Leon Scroggins III <scroggo@google.com>2017-10-18 11:43:29 -0400
committerGravatar Skia Commit-Bot <skia-commit-bot@chromium.org>2017-10-18 17:38:15 +0000
commit5411a60e0d7370a5d47b5049de845a06fe52e98b (patch)
tree8995da6236e7f37419a4d3075c35bbdc36049672
parent1d014105ac8e5968b6d260745c807ab45dfee956 (diff)
Add an Option for orientation on JPEG encodes
Move Origin to its own header so that SkPixmap and SkJpegEncoder need not depend on SkCodec. Add libexif, which is already used by Android, and use it to write the orientation. Write a makefile based on the Android.bp in Android, minus warnings. (libexif has an LGPL license.) Add a test that verifies all the orientations work. Optionally enable writing the orientation (and therefore including libexif). Chromium does not currently need it, and Android does not expose an API that would allow using it. Disable on Windows, where we still have build errors to fix. Bug: skia:7138 Change-Id: Iaeff44c36aebe0e639666979dc00e1b7594bbeb1 Reviewed-on: https://skia-review.googlesource.com/60721 Commit-Queue: Leon Scroggins <scroggo@google.com> Reviewed-by: Mike Klein <mtklein@chromium.org> Reviewed-by: Mike Reed <reed@google.com>
-rw-r--r--BUILD.gn6
-rw-r--r--DEPS1
-rw-r--r--include/codec/SkCodec.h22
-rw-r--r--include/codec/SkEncodedOrigin.h23
-rw-r--r--include/encode/SkJpegEncoder.h10
-rw-r--r--src/codec/SkCodec.cpp5
-rw-r--r--src/codec/SkCodecImageGenerator.cpp24
-rw-r--r--src/codec/SkJpegCodec.cpp16
-rw-r--r--src/codec/SkJpegCodec.h2
-rw-r--r--src/core/SkPixmapPriv.h4
-rw-r--r--src/images/SkJpegEncoder.cpp39
-rw-r--r--tests/ExifTest.cpp49
-rw-r--r--third_party/libexif/BUILD.gn47
13 files changed, 203 insertions, 45 deletions
diff --git a/BUILD.gn b/BUILD.gn
index 2b333af09c..0d3a8d9c3e 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -23,6 +23,7 @@ declare_args() {
skia_use_freetype = is_android || is_fuchsia || is_linux
skia_use_gdi = false
skia_use_icu = !is_fuchsia && !is_ios && !is_win # TODO: Windows
+ skia_use_libexif = !is_win # TODO: Windows
skia_use_libjpeg_turbo = true
skia_use_libpng = true
skia_use_libwebp = !is_fuchsia
@@ -610,6 +611,11 @@ optional("jpeg") {
"src/images/SkJPEGWriteUtility.cpp",
"src/images/SkJpegEncoder.cpp",
]
+
+ if (skia_use_libexif) {
+ public_defines += [ "SK_HAS_EXIF_LIBRARY" ]
+ deps += [ "//third_party/libexif" ]
+ }
}
optional("pdf") {
diff --git a/DEPS b/DEPS
index 4b864432ef..e56bf22bf1 100644
--- a/DEPS
+++ b/DEPS
@@ -12,6 +12,7 @@ deps = {
"third_party/externals/icu" : "https://chromium.googlesource.com/chromium/deps/icu.git@ec9c1133693148470ffe2e5e53576998e3650c1d",
"third_party/externals/imgui" : "https://github.com/ocornut/imgui.git@6384eee34f08cb7eab8d835043e1738e4adcdf75",
"third_party/externals/jsoncpp" : "https://chromium.googlesource.com/external/github.com/open-source-parsers/jsoncpp.git@1.0.0",
+ "third_party/externals/libexif" : "https://android.googlesource.com/platform/external/libexif@60e63383329bdf3018696af6c9a1914e4ff0a920",
"third_party/externals/libjpeg-turbo" : "https://skia.googlesource.com/third_party/libjpeg-turbo.git@6de58e0d28014caf2fc1370145f22fd6d65f63e3",
"third_party/externals/libpng" : "https://skia.googlesource.com/third_party/libpng.git@v1.6.33",
"third_party/externals/libwebp" : "https://chromium.googlesource.com/webm/libwebp.git@v0.6.0",
diff --git a/include/codec/SkCodec.h b/include/codec/SkCodec.h
index 6e9c8280b9..d3035a7a71 100644
--- a/include/codec/SkCodec.h
+++ b/include/codec/SkCodec.h
@@ -14,6 +14,7 @@
#include "SkColorSpaceXform.h"
#include "SkEncodedImageFormat.h"
#include "SkEncodedInfo.h"
+#include "SkEncodedOrigin.h"
#include "SkImageInfo.h"
#include "SkPixmap.h"
#include "SkSize.h"
@@ -170,24 +171,11 @@ public:
const SkEncodedInfo& getEncodedInfo() const { return fEncodedInfo; }
- enum Origin {
- kTopLeft_Origin = 1, // Default
- kTopRight_Origin = 2, // Reflected across y-axis
- kBottomRight_Origin = 3, // Rotated 180
- kBottomLeft_Origin = 4, // Reflected across x-axis
- kLeftTop_Origin = 5, // Reflected across x-axis, Rotated 90 CCW
- kRightTop_Origin = 6, // Rotated 90 CW
- kRightBottom_Origin = 7, // Reflected across x-axis, Rotated 90 CW
- kLeftBottom_Origin = 8, // Rotated 90 CCW
- kDefault_Origin = kTopLeft_Origin,
- kLast_Origin = kLeftBottom_Origin,
- };
-
/**
* Returns the image orientation stored in the EXIF data.
* If there is no EXIF data, or if we cannot read the EXIF data, returns kTopLeft.
*/
- Origin getOrigin() const { return fOrigin; }
+ SkEncodedOrigin getOrigin() const { return fOrigin; }
/**
* Return a size that approximately supports the desired scale factor.
@@ -682,7 +670,7 @@ protected:
XformFormat srcFormat,
std::unique_ptr<SkStream>,
sk_sp<SkColorSpace>,
- Origin = kTopLeft_Origin);
+ SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
/**
* Allows the subclass to set the recommended SkImageInfo
@@ -691,7 +679,7 @@ protected:
const SkImageInfo&,
XformFormat srcFormat,
std::unique_ptr<SkStream>,
- Origin = kTopLeft_Origin);
+ SkEncodedOrigin = kTopLeft_SkEncodedOrigin);
virtual SkISize onGetScaledDimensions(float /*desiredScale*/) const {
// By default, scaling is not supported.
@@ -841,7 +829,7 @@ private:
const XformFormat fSrcXformFormat;
std::unique_ptr<SkStream> fStream;
bool fNeedsRewind;
- const Origin fOrigin;
+ const SkEncodedOrigin fOrigin;
SkImageInfo fDstInfo;
Options fOptions;
diff --git a/include/codec/SkEncodedOrigin.h b/include/codec/SkEncodedOrigin.h
new file mode 100644
index 0000000000..622ade1555
--- /dev/null
+++ b/include/codec/SkEncodedOrigin.h
@@ -0,0 +1,23 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SkEncodedOrigin_DEFINED
+#define SkEncodedOrigin_DEFINED
+// These values match the orientation www.exif.org/Exif2-2.PDF.
+enum SkEncodedOrigin {
+ kTopLeft_SkEncodedOrigin = 1, // Default
+ kTopRight_SkEncodedOrigin = 2, // Reflected across y-axis
+ kBottomRight_SkEncodedOrigin = 3, // Rotated 180
+ kBottomLeft_SkEncodedOrigin = 4, // Reflected across x-axis
+ kLeftTop_SkEncodedOrigin = 5, // Reflected across x-axis, Rotated 90 CCW
+ kRightTop_SkEncodedOrigin = 6, // Rotated 90 CW
+ kRightBottom_SkEncodedOrigin = 7, // Reflected across x-axis, Rotated 90 CW
+ kLeftBottom_SkEncodedOrigin = 8, // Rotated 90 CCW
+ kDefault_SkEncodedOrigin = kTopLeft_SkEncodedOrigin,
+ kLast_SkEncodedOrigin = kLeftBottom_SkEncodedOrigin,
+};
+#endif // SkEncodedOrigin_DEFINED
diff --git a/include/encode/SkJpegEncoder.h b/include/encode/SkJpegEncoder.h
index fd7c204161..a094e3fb9c 100644
--- a/include/encode/SkJpegEncoder.h
+++ b/include/encode/SkJpegEncoder.h
@@ -9,6 +9,9 @@
#define SkJpegEncoder_DEFINED
#include "SkEncoder.h"
+#ifdef SK_HAS_EXIF_LIBRARY
+#include "SkEncodedOrigin.h"
+#endif
class SkJpegEncoderMgr;
class SkWStream;
@@ -62,6 +65,13 @@ public:
*/
AlphaOption fAlphaOption = AlphaOption::kIgnore;
SkTransferFunctionBehavior fBlendBehavior = SkTransferFunctionBehavior::kRespect;
+
+#ifdef SK_HAS_EXIF_LIBRARY
+ /**
+ * Origin to be written to EXIF, if anything other than the default.
+ */
+ SkEncodedOrigin fOrigin = kDefault_SkEncodedOrigin;
+#endif
};
/**
diff --git a/src/codec/SkCodec.cpp b/src/codec/SkCodec.cpp
index c3d99f2793..59978c6fdf 100644
--- a/src/codec/SkCodec.cpp
+++ b/src/codec/SkCodec.cpp
@@ -128,7 +128,7 @@ std::unique_ptr<SkCodec> SkCodec::MakeFromData(sk_sp<SkData> data, SkPngChunkRea
SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info,
XformFormat srcFormat, std::unique_ptr<SkStream> stream,
- sk_sp<SkColorSpace> colorSpace, Origin origin)
+ sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin)
: fEncodedInfo(info)
, fSrcInfo(info.makeImageInfo(width, height, std::move(colorSpace)))
, fSrcXformFormat(srcFormat)
@@ -142,7 +142,8 @@ SkCodec::SkCodec(int width, int height, const SkEncodedInfo& info,
{}
SkCodec::SkCodec(const SkEncodedInfo& info, const SkImageInfo& imageInfo,
- XformFormat srcFormat, std::unique_ptr<SkStream> stream, Origin origin)
+ XformFormat srcFormat, std::unique_ptr<SkStream> stream,
+ SkEncodedOrigin origin)
: fEncodedInfo(info)
, fSrcInfo(imageInfo)
, fSrcXformFormat(srcFormat)
diff --git a/src/codec/SkCodecImageGenerator.cpp b/src/codec/SkCodecImageGenerator.cpp
index e8d7d3038a..741c8e393d 100644
--- a/src/codec/SkCodecImageGenerator.cpp
+++ b/src/codec/SkCodecImageGenerator.cpp
@@ -15,23 +15,23 @@
#define kSwapXY SkPixmapPriv::kSwapXY
const uint8_t gOrientationFlags[] = {
- 0, // kTopLeft_Origin
- kMirrorX, // kTopRight_Origin
- kMirrorX | kMirrorY, // kBottomRight_Origin
- kMirrorY, // kBottomLeft_Origin
- kSwapXY, // kLeftTop_Origin
- kMirrorX | kSwapXY, // kRightTop_Origin
- kMirrorX | kMirrorY | kSwapXY, // kRightBottom_Origin
- kMirrorY | kSwapXY, // kLeftBottom_Origin
+ 0, // kTopLeft_SkEncodedOrigin
+ kMirrorX, // kTopRight_SkEncodedOrigin
+ kMirrorX | kMirrorY, // kBottomRight_SkEncodedOrigin
+ kMirrorY, // kBottomLeft_SkEncodedOrigin
+ kSwapXY, // kLeftTop_SkEncodedOrigin
+ kMirrorX | kSwapXY, // kRightTop_SkEncodedOrigin
+ kMirrorX | kMirrorY | kSwapXY, // kRightBottom_SkEncodedOrigin
+ kMirrorY | kSwapXY, // kLeftBottom_SkEncodedOrigin
};
-SkPixmapPriv::OrientFlags SkPixmapPriv::OriginToOrient(SkCodec::Origin o) {
+SkPixmapPriv::OrientFlags SkPixmapPriv::OriginToOrient(SkEncodedOrigin o) {
unsigned io = static_cast<int>(o) - 1;
SkASSERT(io < SK_ARRAY_COUNT(gOrientationFlags));
return static_cast<SkPixmapPriv::OrientFlags>(gOrientationFlags[io]);
}
-static bool should_swap_width_height(SkCodec::Origin o) {
+static bool should_swap_width_height(SkEncodedOrigin o) {
return SkToBool(SkPixmapPriv::OriginToOrient(o) & kSwapXY);
}
@@ -73,12 +73,12 @@ SkData* SkCodecImageGenerator::onRefEncodedData() {
bool SkCodecImageGenerator::onGetPixels(const SkImageInfo& requestInfo, void* requestPixels,
size_t requestRowBytes, const Options& opts) {
- const SkCodec::Origin origin = fCodec->getOrigin();
+ const auto origin = fCodec->getOrigin();
const SkPixmap request(requestInfo, requestPixels, requestRowBytes);
const SkPixmap* codecMap = &request;
SkAutoPixmapStorage storage; // used if we have to post-orient the output from the codec
- if (origin != SkCodec::kTopLeft_Origin) {
+ if (origin != kTopLeft_SkEncodedOrigin) {
SkImageInfo info = requestInfo;
if (should_swap_width_height(origin)) {
info = swap_width_height(info);
diff --git a/src/codec/SkJpegCodec.cpp b/src/codec/SkJpegCodec.cpp
index be5c9f7942..062f05bd01 100644
--- a/src/codec/SkJpegCodec.cpp
+++ b/src/codec/SkJpegCodec.cpp
@@ -45,7 +45,7 @@ static uint32_t get_endian_int(const uint8_t* data, bool littleEndian) {
const uint32_t kExifHeaderSize = 14;
const uint32_t kExifMarker = JPEG_APP0 + 1;
-static bool is_orientation_marker(jpeg_marker_struct* marker, SkCodec::Origin* orientation) {
+static bool is_orientation_marker(jpeg_marker_struct* marker, SkEncodedOrigin* orientation) {
if (kExifMarker != marker->marker || marker->data_length < kExifHeaderSize) {
return false;
}
@@ -87,8 +87,8 @@ static bool is_orientation_marker(jpeg_marker_struct* marker, SkCodec::Origin* o
uint32_t count = get_endian_int(data + 4, littleEndian);
if (kOriginTag == tag && kOriginType == type && 1 == count) {
uint16_t val = get_endian_short(data + 8, littleEndian);
- if (0 < val && val <= SkCodec::kLast_Origin) {
- *orientation = (SkCodec::Origin) val;
+ if (0 < val && val <= kLast_SkEncodedOrigin) {
+ *orientation = (SkEncodedOrigin) val;
return true;
}
}
@@ -97,15 +97,15 @@ static bool is_orientation_marker(jpeg_marker_struct* marker, SkCodec::Origin* o
return false;
}
-static SkCodec::Origin get_exif_orientation(jpeg_decompress_struct* dinfo) {
- SkCodec::Origin orientation;
+static SkEncodedOrigin get_exif_orientation(jpeg_decompress_struct* dinfo) {
+ SkEncodedOrigin orientation;
for (jpeg_marker_struct* marker = dinfo->marker_list; marker; marker = marker->next) {
if (is_orientation_marker(marker, &orientation)) {
return orientation;
}
}
- return SkCodec::kDefault_Origin;
+ return kDefault_SkEncodedOrigin;
}
static bool is_icc_marker(jpeg_marker_struct* marker) {
@@ -227,7 +227,7 @@ SkCodec::Result SkJpegCodec::ReadHeader(SkStream* stream, SkCodec** codecOut,
// Create image info object and the codec
SkEncodedInfo info = SkEncodedInfo::Make(color, SkEncodedInfo::kOpaque_Alpha, 8);
- Origin orientation = get_exif_orientation(decoderMgr->dinfo());
+ SkEncodedOrigin orientation = get_exif_orientation(decoderMgr->dinfo());
sk_sp<SkData> iccData = get_icc_profile(decoderMgr->dinfo());
sk_sp<SkColorSpace> colorSpace = nullptr;
if (iccData) {
@@ -284,7 +284,7 @@ std::unique_ptr<SkCodec> SkJpegCodec::MakeFromStream(std::unique_ptr<SkStream> s
SkJpegCodec::SkJpegCodec(int width, int height, const SkEncodedInfo& info,
std::unique_ptr<SkStream> stream, JpegDecoderMgr* decoderMgr,
- sk_sp<SkColorSpace> colorSpace, Origin origin)
+ sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin)
: INHERITED(width, height, info, SkColorSpaceXform::kRGBA_8888_ColorFormat, std::move(stream),
std::move(colorSpace), origin)
, fDecoderMgr(decoderMgr)
diff --git a/src/codec/SkJpegCodec.h b/src/codec/SkJpegCodec.h
index fd8ee63d6c..41814d2ead 100644
--- a/src/codec/SkJpegCodec.h
+++ b/src/codec/SkJpegCodec.h
@@ -107,7 +107,7 @@ private:
* takes ownership
*/
SkJpegCodec(int width, int height, const SkEncodedInfo& info, std::unique_ptr<SkStream> stream,
- JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, Origin origin);
+ JpegDecoderMgr* decoderMgr, sk_sp<SkColorSpace> colorSpace, SkEncodedOrigin origin);
/*
* Checks if the conversion between the input image and the requested output
diff --git a/src/core/SkPixmapPriv.h b/src/core/SkPixmapPriv.h
index dfc9ce2dd0..1d677fdc46 100644
--- a/src/core/SkPixmapPriv.h
+++ b/src/core/SkPixmapPriv.h
@@ -9,7 +9,7 @@
#define SkPixmapPriv_DEFINED
#include "SkPixmap.h"
-#include "SkCodec.h"
+#include "SkEncodedOrigin.h"
class SkPixmapPriv {
public:
@@ -20,7 +20,7 @@ public:
kSwapXY = 1 << 2,
};
- static OrientFlags OriginToOrient(SkCodec::Origin);
+ static OrientFlags OriginToOrient(SkEncodedOrigin);
/**
* Copy the pixels in this pixmap into dst, applying the orientation transformations specified
diff --git a/src/images/SkJpegEncoder.cpp b/src/images/SkJpegEncoder.cpp
index 9fade2449a..3f818286a1 100644
--- a/src/images/SkJpegEncoder.cpp
+++ b/src/images/SkJpegEncoder.cpp
@@ -11,6 +11,7 @@
#include "SkColorData.h"
#include "SkColorSpace_Base.h"
+#include "SkEncodedOrigin.h"
#include "SkImageEncoderFns.h"
#include "SkImageInfoPriv.h"
#include "SkJpegEncoder.h"
@@ -18,6 +19,12 @@
#include "SkStream.h"
#include "SkTemplates.h"
+#ifdef SK_HAS_EXIF_LIBRARY
+#include "libexif/exif-byte-order.h"
+#include "libexif/exif-data.h"
+#include "libexif/exif-format.h"
+#endif
+
#include <stdio.h>
extern "C" {
@@ -213,6 +220,38 @@ std::unique_ptr<SkEncoder> SkJpegEncoder::Make(SkWStream* dst, const SkPixmap& s
jpeg_write_marker(encoderMgr->cinfo(), kICCMarker, markerData->bytes(), markerData->size());
}
+#ifdef SK_HAS_EXIF_LIBRARY
+ if (options.fOrigin != kDefault_SkEncodedOrigin) {
+ // Create ExifData.
+ const auto kByteOrder = EXIF_BYTE_ORDER_INTEL;
+ SkAutoTCallVProc<ExifData, exif_data_unref> exif(exif_data_new());
+ exif_data_set_option(exif.get(), EXIF_DATA_OPTION_FOLLOW_SPECIFICATION);
+ exif_data_set_data_type(exif.get(), EXIF_DATA_TYPE_COMPRESSED);
+ exif_data_set_byte_order(exif.get(), kByteOrder);
+ exif_data_fix(exif.get());
+
+ // Create entry for rotation.
+ SkAutoTCallVProc<ExifMem, exif_mem_unref> mem(exif_mem_new_default());
+ SkAutoTCallVProc<ExifEntry, exif_entry_unref> entry(exif_entry_new_mem(mem));
+ size_t size = exif_format_get_size(EXIF_FORMAT_SHORT);
+ entry->data = reinterpret_cast<unsigned char*>(exif_mem_alloc(mem, size));
+ entry->size = size;
+ entry->tag = EXIF_TAG_ORIENTATION;
+ entry->components = 1;
+ entry->format = EXIF_FORMAT_SHORT;
+ exif_content_add_entry(exif->ifd[EXIF_IFD_0], entry);
+ exif_set_short(entry->data, kByteOrder, (ExifShort) options.fOrigin);
+
+ // Serialize the data.
+ unsigned char* exif_data;
+ unsigned int exif_data_len;
+ exif_data_save_data(exif.get(), &exif_data, &exif_data_len);
+ const uint32_t kExifMarker = JPEG_APP0 + 1;
+ jpeg_write_marker(encoderMgr->cinfo(), kExifMarker, exif_data, exif_data_len);
+ sk_free(exif_data);
+ }
+#endif
+
return std::unique_ptr<SkJpegEncoder>(new SkJpegEncoder(std::move(encoderMgr), src));
}
diff --git a/tests/ExifTest.cpp b/tests/ExifTest.cpp
index 181f0f9f04..f2eec7a98e 100644
--- a/tests/ExifTest.cpp
+++ b/tests/ExifTest.cpp
@@ -6,7 +6,12 @@
*/
#include "Resources.h"
+#include "SkBitmap.h"
#include "SkCodec.h"
+#include "SkColor.h"
+#include "SkImageInfo.h"
+#include "SkJpegEncoder.h"
+#include "SkStream.h"
#include "Test.h"
DEF_TEST(ExifOrientation, r) {
@@ -18,11 +23,49 @@ DEF_TEST(ExifOrientation, r) {
std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream)));
REPORTER_ASSERT(r, nullptr != codec);
- SkCodec::Origin origin = codec->getOrigin();
- REPORTER_ASSERT(r, SkCodec::kTopRight_Origin == origin);
+ SkEncodedOrigin origin = codec->getOrigin();
+ REPORTER_ASSERT(r, kTopRight_SkEncodedOrigin == origin);
codec = SkCodec::MakeFromStream(GetResourceAsStream("mandrill_512_q075.jpg"));
REPORTER_ASSERT(r, nullptr != codec);
origin = codec->getOrigin();
- REPORTER_ASSERT(r, SkCodec::kTopLeft_Origin == origin);
+ REPORTER_ASSERT(r, kTopLeft_SkEncodedOrigin == origin);
}
+
+#ifdef SK_HAS_EXIF_LIBRARY
+DEF_TEST(ExifWriteOrientation, r) {
+ SkBitmap bm;
+ bm.allocPixels(SkImageInfo::MakeN32Premul(100, 100));
+ bm.eraseColor(SK_ColorBLUE);
+ SkPixmap pm;
+ if (!bm.peekPixels(&pm)) {
+ ERRORF(r, "failed to peek pixels");
+ return;
+ }
+ for (auto o : { kTopLeft_SkEncodedOrigin,
+ kTopRight_SkEncodedOrigin,
+ kBottomRight_SkEncodedOrigin,
+ kBottomLeft_SkEncodedOrigin,
+ kLeftTop_SkEncodedOrigin,
+ kRightTop_SkEncodedOrigin,
+ kRightBottom_SkEncodedOrigin,
+ kLeftBottom_SkEncodedOrigin }) {
+ SkDynamicMemoryWStream stream;
+ SkJpegEncoder::Options options;
+ options.fOrigin = o;
+ if (!SkJpegEncoder::Encode(&stream, pm, options)) {
+ ERRORF(r, "Failed to encode with orientation %i", o);
+ return;
+ }
+
+ auto data = stream.detachAsData();
+ auto codec = SkCodec::MakeFromData(std::move(data));
+ if (!codec) {
+ ERRORF(r, "Failed to create a codec with orientation %i", o);
+ return;
+ }
+
+ REPORTER_ASSERT(r, codec->getOrigin() == o);
+ }
+}
+#endif
diff --git a/third_party/libexif/BUILD.gn b/third_party/libexif/BUILD.gn
new file mode 100644
index 0000000000..9bd6c72b28
--- /dev/null
+++ b/third_party/libexif/BUILD.gn
@@ -0,0 +1,47 @@
+# Copyright 2017 Google Inc.
+#
+# Use of this source code is governed by a BSD-style license that can be
+# found in the LICENSE file.
+
+declare_args() {
+ skia_use_system_exif = is_official_build
+}
+
+import("../third_party.gni")
+
+if (skia_use_system_exif) {
+ system("libexif") {
+ libs = [ "exif" ]
+ }
+} else {
+ third_party("libexif") {
+ public_include_dirs = [ "../externals/libexif" ]
+
+ sources = [
+ "../externals/libexif/libexif/canon/exif-mnote-data-canon.c",
+ "../externals/libexif/libexif/canon/mnote-canon-entry.c",
+ "../externals/libexif/libexif/canon/mnote-canon-tag.c",
+ "../externals/libexif/libexif/exif-byte-order.c",
+ "../externals/libexif/libexif/exif-content.c",
+ "../externals/libexif/libexif/exif-data.c",
+ "../externals/libexif/libexif/exif-entry.c",
+ "../externals/libexif/libexif/exif-format.c",
+ "../externals/libexif/libexif/exif-ifd.c",
+ "../externals/libexif/libexif/exif-loader.c",
+ "../externals/libexif/libexif/exif-log.c",
+ "../externals/libexif/libexif/exif-mem.c",
+ "../externals/libexif/libexif/exif-mnote-data.c",
+ "../externals/libexif/libexif/exif-tag.c",
+ "../externals/libexif/libexif/exif-utils.c",
+ "../externals/libexif/libexif/fuji/exif-mnote-data-fuji.c",
+ "../externals/libexif/libexif/fuji/mnote-fuji-entry.c",
+ "../externals/libexif/libexif/fuji/mnote-fuji-tag.c",
+ "../externals/libexif/libexif/olympus/exif-mnote-data-olympus.c",
+ "../externals/libexif/libexif/olympus/mnote-olympus-entry.c",
+ "../externals/libexif/libexif/olympus/mnote-olympus-tag.c",
+ "../externals/libexif/libexif/pentax/exif-mnote-data-pentax.c",
+ "../externals/libexif/libexif/pentax/mnote-pentax-entry.c",
+ "../externals/libexif/libexif/pentax/mnote-pentax-tag.c",
+ ]
+ }
+}