Add DaydreamVR native libraries and services
Upstreaming the main VR system components from master-dreamos-dev
into goog/master.
Bug: None
Test: `m -j32` succeeds. Sailfish boots and basic_vr sample app works
Change-Id: I853015872afc443aecee10411ef2d6b79184d051
diff --git a/libs/vr/libimageio/Android.mk b/libs/vr/libimageio/Android.mk
new file mode 100644
index 0000000..b3b88ac
--- /dev/null
+++ b/libs/vr/libimageio/Android.mk
@@ -0,0 +1,23 @@
+LOCAL_PATH := $(call my-dir)
+
+sourceFiles := \
+ image_io.cpp \
+ image_io_png.cpp \
+ image_io_ppm.cpp
+
+includeFiles := \
+ $(LOCAL_PATH)/include
+
+sharedLibraries := \
+ libcutils \
+ libpng
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(sourceFiles)
+LOCAL_C_INCLUDES += $(includeFiles)
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(includeFiles)
+LOCAL_SHARED_LIBRARIES := $(sharedLibraries)
+LOCAL_CFLAGS := -Wall -Wextra
+LOCAL_MODULE := libimageio
+LOCAL_MODULE_TAGS := optional
+include $(BUILD_STATIC_LIBRARY)
diff --git a/libs/vr/libimageio/image_io.cpp b/libs/vr/libimageio/image_io.cpp
new file mode 100644
index 0000000..5ad6c2d
--- /dev/null
+++ b/libs/vr/libimageio/image_io.cpp
@@ -0,0 +1,92 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io.h>
+
+#include <algorithm>
+#include <memory>
+#include <string>
+
+#include <private/dvr/image_io_base.h>
+#include <private/dvr/image_io_logging.h>
+#include <private/dvr/image_io_png.h>
+#include <private/dvr/image_io_ppm.h>
+
+namespace {
+
+// Returns true if |str| ends with |suffix|.
+bool EndsWith(const std::string& str, const std::string& suffix) {
+ if (str.length() < suffix.length())
+ return false;
+
+ return std::equal(suffix.rbegin(), suffix.rend(), str.rbegin());
+}
+
+// Returns lower case copy of the input string.
+std::string ToLower(std::string str) {
+ std::transform(str.begin(), str.end(), str.begin(),
+ [](char x) { return std::tolower(x); });
+ return str;
+}
+
+} // namespace
+
+std::unique_ptr<ImageIoReader> ImageIoReader::Create(const char* filename) {
+ std::unique_ptr<ImageIoReader> reader;
+ std::string filename_lower = ToLower(filename);
+
+ if (EndsWith(filename_lower, ".ppm"))
+ reader.reset(new ImageIoPpmReader(filename));
+
+ if (!reader) {
+ ALOGE("Unknown/unsupported image file format.");
+ return nullptr;
+ }
+
+ return reader;
+}
+
+std::unique_ptr<ImageIoWriter> ImageIoWriter::Create(const char* filename,
+ int width, int height,
+ const uint8_t* image) {
+ std::unique_ptr<ImageIoWriter> writer;
+ std::string filename_lower = ToLower(filename);
+
+ if (EndsWith(filename_lower, ".ppm"))
+ writer.reset(new ImageIoPpmWriter(filename, width, height, image));
+ else if (EndsWith(filename_lower, ".png"))
+ writer.reset(new ImageIoPngWriter(filename, width, height, image));
+
+ if (!writer) {
+ ALOGE("Unknown/unsupported image file format.");
+ return nullptr;
+ }
+
+ return writer;
+}
+
+extern "C" {
+
+bool image_io_write_rgb888(const char* filename, int width, int height,
+ const uint8_t* image) {
+ auto writer = ImageIoWriter::Create(filename, width, height, image);
+ if (!writer)
+ return false;
+ return writer->WriteRgb888();
+}
+
+bool image_io_read_rgb888(const char* filename, int* width, int* height,
+ uint8_t** image) {
+ auto reader = ImageIoReader::Create(filename);
+ if (!reader)
+ return false;
+ if (!reader->ReadRgb888())
+ return false;
+ *width = reader->width();
+ *height = reader->height();
+ *image = reader->ReleaseImage();
+ return true;
+}
+
+void image_io_release_buffer(uint8_t* image) { delete[] image; }
+
+} // extern "C"
diff --git a/libs/vr/libimageio/image_io_png.cpp b/libs/vr/libimageio/image_io_png.cpp
new file mode 100644
index 0000000..e0a118b
--- /dev/null
+++ b/libs/vr/libimageio/image_io_png.cpp
@@ -0,0 +1,87 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io_png.h>
+
+#include <fstream>
+#include <string>
+#include <vector>
+
+#include <private/dvr/image_io_logging.h>
+
+#include "png.h"
+
+namespace {
+
+void WriteChunkCallback(png_structp out_ptr, png_bytep chunk_ptr,
+ png_size_t chunk_size) {
+ auto* writer = static_cast<ImageIoPngWriter*>(png_get_io_ptr(out_ptr));
+ const char* chunk = reinterpret_cast<const char*>(chunk_ptr);
+ writer->WriteChunk(chunk, chunk_size);
+}
+
+} // namespace
+
+ImageIoPngWriter::ImageIoPngWriter(const char* filename, int width, int height,
+ const uint8_t* image)
+ : ImageIoWriter(filename, width, height, image),
+ out_(filename_),
+ write_failed_(false) {}
+
+bool ImageIoPngWriter::WriteChunk(const char* chunk, int chunk_size) {
+ out_.write(chunk, chunk_size);
+ if (!out_) {
+ if (write_failed_) {
+ // Error was already logged once.
+ return false;
+ }
+
+ ALOGE("Failed to write .png image to %s.", filename_.c_str());
+ write_failed_ = true;
+ return false;
+ }
+ return true;
+}
+
+// Writes RGB888 image to png file.
+// Refactored from Chromium:
+// WebKit/Source/platform/image-encoders/skia/PNGImageEncoder.cpp
+bool ImageIoPngWriter::WriteRgb888() {
+ if (width_ <= 0 || height_ <= 0) {
+ ALOGE("Invalid width or height.");
+ return false;
+ }
+
+ if (!out_) {
+ ALOGE("Failed to open output file %s.", filename_.c_str());
+ return false;
+ }
+
+ png_struct* png = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
+ png_info* info = png_create_info_struct(png);
+ if (!png || !info || setjmp(png_jmpbuf(png))) {
+ png_destroy_write_struct(png ? &png : 0, info ? &info : 0);
+ return false;
+ }
+
+ png_set_compression_level(png, 3);
+ png_set_filter(png, PNG_FILTER_TYPE_BASE, PNG_FILTER_SUB);
+
+ png_set_write_fn(png, this, WriteChunkCallback, 0);
+ png_set_IHDR(png, info, width_, height_, 8, PNG_COLOR_TYPE_RGB, 0, 0, 0);
+ png_write_info(png, info);
+
+ unsigned char* pixels =
+ reinterpret_cast<unsigned char*>(const_cast<uint8_t*>(image_));
+ const size_t stride = width_ * 3;
+ for (int y = 0; y < height_; ++y) {
+ png_write_row(png, pixels);
+ if (write_failed_)
+ return false;
+ pixels += stride;
+ }
+
+ png_write_end(png, info);
+ png_destroy_write_struct(&png, &info);
+
+ return !write_failed_;
+}
diff --git a/libs/vr/libimageio/image_io_ppm.cpp b/libs/vr/libimageio/image_io_ppm.cpp
new file mode 100644
index 0000000..2411888
--- /dev/null
+++ b/libs/vr/libimageio/image_io_ppm.cpp
@@ -0,0 +1,93 @@
+#define LOG_TAG "ImageIo"
+
+#include <private/dvr/image_io_ppm.h>
+
+#include <cwctype>
+#include <fstream>
+#include <string>
+
+#include <private/dvr/image_io_logging.h>
+
+bool ImageIoPpmWriter::WriteRgb888() {
+ std::ofstream out(filename_);
+ if (!out) {
+ ALOGE("Failed to open output file %s.", filename_.c_str());
+ return false;
+ }
+
+ // Write a PPM header. See http://netpbm.sourceforge.net/doc/ppm.html for
+ // the format specification.
+ constexpr int maximum_intensity = 255;
+ out << "P6\n"
+ << width_ << "\n"
+ << height_ << "\n"
+ << maximum_intensity << "\n";
+
+ // Write out the image itself.
+ out.write(reinterpret_cast<const char*>(image_), 3 * width_ * height_);
+
+ if (!out) {
+ ALOGE("Failed to write .ppm image to %s.", filename_.c_str());
+ return false;
+ }
+ return true;
+}
+
+bool ImageIoPpmReader::ReadRgb888() {
+ std::ifstream in(filename_);
+ if (!in) {
+ ALOGE("Failed to open input file %s.", filename_.c_str());
+ return false;
+ }
+
+ // Read PPM header. See http://netpbm.sourceforge.net/doc/ppm.html for
+ // the format specification.
+ char magic_number[2];
+ in.read(magic_number, 2);
+ if (magic_number[0] != 'P' || magic_number[1] != '6') {
+ ALOGE("Failed to read PPM, not a P6 file %s.", filename_.c_str());
+ return false;
+ }
+
+ int maximum_intensity = 0;
+
+ in >> width_;
+ in >> height_;
+ in >> maximum_intensity;
+
+ char delimiter;
+ in.read(&delimiter, 1);
+
+ if (!iswspace(delimiter) || width_ <= 0 || height_ <= 0 ||
+ maximum_intensity <= 0) {
+ ALOGE("Failed to parse PPM header for %s.", filename_.c_str());
+ return false;
+ }
+
+ if (maximum_intensity != 255) {
+ ALOGE("Failed to read PPM, only 8-bit depth supported %s.",
+ filename_.c_str());
+ return false;
+ }
+
+ // Read RGB data.
+ const int data_begin = in.tellg();
+ in.seekg(0, in.end);
+ const int data_end = in.tellg();
+ in.seekg(data_begin, in.beg);
+
+ const int data_size = data_end - data_begin;
+ if (data_size != 3 * width_ * height_) {
+ ALOGE("Failed to read PPM, unexpected data size %s.", filename_.c_str());
+ return false;
+ }
+
+ image_.reset(new uint8_t[data_size]);
+ char* data = reinterpret_cast<char*>(image_.get());
+
+ const auto it_data_begin = std::istreambuf_iterator<char>(in);
+ const auto it_data_end = std::istreambuf_iterator<char>();
+ std::copy(it_data_begin, it_data_end, data);
+
+ return true;
+}
diff --git a/libs/vr/libimageio/include/CPPLINT.cfg b/libs/vr/libimageio/include/CPPLINT.cfg
new file mode 100644
index 0000000..2f8a3c0
--- /dev/null
+++ b/libs/vr/libimageio/include/CPPLINT.cfg
@@ -0,0 +1 @@
+filter=-build/header_guard
diff --git a/libs/vr/libimageio/include/private/dvr/image_io.h b/libs/vr/libimageio/include/private/dvr/image_io.h
new file mode 100644
index 0000000..5cb115d
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io.h
@@ -0,0 +1,32 @@
+#ifndef DVR_IMAGE_IO_H_
+#define DVR_IMAGE_IO_H_
+
+#include <stdbool.h>
+#include <stdint.h>
+
+// Supported filetypes.
+#define DVR_IMAGE_IO_SUPPORTED_WRITE "png, ppm"
+#define DVR_IMAGE_IO_SUPPORTED_READ "ppm"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+// Writes an RGB888 image to file. Intended file type is autodetected
+// based on the extension. Currently supported formats: PNG, PPM.
+bool image_io_write_rgb888(const char* filename, int width, int height,
+ const uint8_t* image);
+
+// Reads an RGB888 image from file. Image buffer needs to be released with
+// image_io_release_image. Currently supported formats: PPM.
+bool image_io_read_rgb888(const char* filename, int* width, int* height,
+ uint8_t** image);
+
+// Releases image buffer allocated within the library.
+void image_io_release_buffer(uint8_t* image);
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
+#endif // DVR_IMAGE_IO_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_base.h b/libs/vr/libimageio/include/private/dvr/image_io_base.h
new file mode 100644
index 0000000..009cad4
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_base.h
@@ -0,0 +1,56 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_BASE_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_BASE_H_
+
+#include <memory>
+#include <string>
+
+class ImageIoReader {
+ public:
+ virtual ~ImageIoReader() {}
+
+ static std::unique_ptr<ImageIoReader> Create(const char* filename);
+
+ virtual bool ReadRgb888() = 0;
+
+ int width() const { return width_; }
+
+ int height() const { return height_; }
+
+ uint8_t* ReleaseImage() { return image_.release(); }
+
+ protected:
+ int width_;
+ int height_;
+ std::unique_ptr<uint8_t[]> image_;
+ const std::string filename_;
+
+ explicit ImageIoReader(const char* filename)
+ : width_(0), height_(0), filename_(filename) {}
+
+ ImageIoReader() = delete;
+};
+
+class ImageIoWriter {
+ public:
+ virtual ~ImageIoWriter() {}
+
+ static std::unique_ptr<ImageIoWriter> Create(const char* filename, int width,
+ int height,
+ const uint8_t* image);
+
+ virtual bool WriteRgb888() = 0;
+
+ protected:
+ const int width_;
+ const int height_;
+ const uint8_t* image_;
+ const std::string filename_;
+
+ ImageIoWriter(const char* filename, int width, int height,
+ const uint8_t* image)
+ : width_(width), height_(height), image_(image), filename_(filename) {}
+
+ ImageIoWriter() = delete;
+};
+
+#endif // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_BASE_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_logging.h b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
new file mode 100644
index 0000000..3c0f2a5
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_logging.h
@@ -0,0 +1,39 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
+
+// This header acts as cutils/log.h if LOG_TO_STDERR is not defined.
+// If LOG_TO_STDERR is defined, then android logging macros (such as ALOGE)
+// would log to stderr. This is useful if the code is also being used/tested on
+// a desktop.
+
+#ifdef LOG_TO_STDERR
+#include <stdarg.h>
+#include <cstdio>
+
+#ifndef LOG_TAG
+#define LOG_TAG " "
+#endif // LOG_TAG
+
+inline void LogToStderr(const char* severity, const char* fmt, ...) {
+ fprintf(stderr, "%s %s: ", LOG_TAG, severity);
+ va_list args;
+ va_start(args, fmt);
+ vfprintf(stderr, fmt, args);
+ va_end(args);
+ fprintf(stderr, "\n");
+ fflush(stderr);
+}
+
+#define ALOGE(fmt, ...) LogToStderr("ERROR", fmt, ##__VA_ARGS__)
+
+#define ALOGW(fmt, ...) LogToStderr("WARNING", fmt, ##__VA_ARGS__)
+
+#define ALOGI(fmt, ...) LogToStderr("INFO", fmt, ##__VA_ARGS__)
+
+#define ALOGV(fmt, ...) LogToStderr("VERBOSE", fmt, ##__VA_ARGS__)
+
+#else // LOG_TO_STDERR
+#include <cutils/log.h>
+#endif // LOG_TO_STDERR
+
+#endif // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_LOGGING_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_png.h b/libs/vr/libimageio/include/private/dvr/image_io_png.h
new file mode 100644
index 0000000..e3b19db
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_png.h
@@ -0,0 +1,24 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PNG_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PNG_H_
+
+#include <fstream>
+
+#include <private/dvr/image_io_base.h>
+
+class ImageIoPngWriter : public ImageIoWriter {
+ public:
+ bool WriteRgb888() override;
+
+ bool WriteChunk(const char* chunk, int chunk_size);
+
+ private:
+ ImageIoPngWriter(const char* filename, int width, int height,
+ const uint8_t* image);
+
+ std::ofstream out_;
+ bool write_failed_;
+
+ friend class ImageIoWriter;
+};
+
+#endif // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PNG_H_
diff --git a/libs/vr/libimageio/include/private/dvr/image_io_ppm.h b/libs/vr/libimageio/include/private/dvr/image_io_ppm.h
new file mode 100644
index 0000000..00264bd
--- /dev/null
+++ b/libs/vr/libimageio/include/private/dvr/image_io_ppm.h
@@ -0,0 +1,28 @@
+#ifndef LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PPM_H_
+#define LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PPM_H_
+
+#include <private/dvr/image_io_base.h>
+
+class ImageIoPpmReader : public ImageIoReader {
+ public:
+ bool ReadRgb888() override;
+
+ private:
+ explicit ImageIoPpmReader(const char* filename) : ImageIoReader(filename) {}
+
+ friend class ImageIoReader;
+};
+
+class ImageIoPpmWriter : public ImageIoWriter {
+ public:
+ bool WriteRgb888() override;
+
+ private:
+ ImageIoPpmWriter(const char* filename, int width, int height,
+ const uint8_t* image)
+ : ImageIoWriter(filename, width, height, image) {}
+
+ friend class ImageIoWriter;
+};
+
+#endif // LIB_LIBIMAGEIO_PRIVATE_DREAMOS_IMAGE_IO_PPM_H_