Commit d848a857 authored by ckitagawa's avatar ckitagawa Committed by Commit Bot

[Paint Preview] File IO Utilities for working with Skia

Create new component for Paint Previews (Internal Link: go/fdt-design).
The reasoning for creating this component is outlined in the README.

Adds some basic file IO interfaces for working with Skia data streams.

Design Doc [Internal]: go/fdt-design

Part of landing:
https://chromium-review.googlesource.com/c/chromium/src/+/1786583

Bug: 1005949
Change-Id: I6e09aaa840766806527e5baab2224d2ccfc5945a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1814875Reviewed-by: default avatarNico Weber <thakis@chromium.org>
Reviewed-by: default avatarColin Blundell <blundell@chromium.org>
Reviewed-by: default avatarIan Vollick <vollick@chromium.org>
Commit-Queue: Calder Kitagawa <ckitagawa@chromium.org>
Cr-Commit-Position: refs/heads/master@{#698902}
parent 2e46b48d
......@@ -118,6 +118,7 @@ test("components_unittests") {
"//components/omnibox/browser:unit_tests",
"//components/open_from_clipboard:unit_tests",
"//components/os_crypt:unit_tests",
"//components/paint_preview/common:unit_tests",
"//components/password_manager/core/browser:unit_tests",
"//components/password_manager/core/common:unit_tests",
"//components/payments/core:unit_tests",
......
include_rules = [
"+third_party/skia/include/core",
]
mahmoudi@chromium.org
vollick@chromium.org
yfriedman@chromium.org
# Component: Internals>FreezeDriedTabs
# Paint Previews
_AKA Freeze Dried Tabs_
This is a WIP directory that contains code for generating and processing paint
previews of pages. A paint preview is a collection of Skia Pictures representing
the visual contents of a page along with the links on the page. The preview can
be composited and played without a renderer process as a low-fidelity and
low-cost alternative to a tab in various contexts.
## Why //components?
This directory facilitates sharing code between Blink and the browser
process. This has the additional benefit of keeping most of the code
centralized to this directory. Parts of the code are consumed in;
* `//cc`
* `//chrome`
* `//content`
* `//third_party/blink`
## Directory Structure (WIP)
* `common/` - Shared code; mojo, protos, and C++ code.
# Copyright 2019 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//testing/test.gni")
static_library("common") {
sources = [
"file_stream.cc",
"file_stream.h",
]
deps = [
"//base",
"//skia",
]
}
source_set("unit_tests") {
testonly = true
sources = [
"file_stream_unittest.cc",
]
deps = [
":common",
"//base",
"//base/test:test_support",
"//skia",
"//testing/gmock",
"//testing/gtest",
]
}
test("paint_preview_common_unit_tests") {
deps = [
":unit_tests",
"//base",
"//base/test:test_support",
"//components/test:run_all_unittests",
]
}
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/paint_preview/common/file_stream.h"
#include <stdint.h>
#include <utility>
namespace paint_preview {
// FileWStream
FileWStream::FileWStream(base::File file)
: file_(std::move(file)), bytes_written_(0) {
DCHECK(file_.IsValid());
}
// Close() is called in the destructor of |file_|.
FileWStream::~FileWStream() = default;
bool FileWStream::write(const void* buffer, size_t size) {
if (!file_.IsValid())
return false;
int bytes =
file_.WriteAtCurrentPos(reinterpret_cast<const char*>(buffer), size);
if (bytes < 0)
return false;
bytes_written_ += bytes;
if (static_cast<size_t>(bytes) != size)
return false;
return true;
}
void FileWStream::flush() {
if (!file_.IsValid())
return;
file_.Flush();
}
size_t FileWStream::bytesWritten() const {
return bytes_written_;
}
void FileWStream::Close() {
if (!file_.IsValid())
return;
file_.Close();
}
// FileRStream
FileRStream::FileRStream(base::File file)
: file_(std::move(file)), length_(file_.GetLength()), bytes_read_(0) {
DCHECK(file_.IsValid());
}
FileRStream::~FileRStream() = default;
size_t FileRStream::read(void* buffer, size_t size) {
if (!file_.IsValid())
return 0;
int64_t num_bytes;
if (!buffer) {
int64_t origin = file_.Seek(base::File::FROM_CURRENT, 0);
if (origin < 0)
return 0;
num_bytes = file_.Seek(base::File::FROM_CURRENT, size);
if (num_bytes < 0)
return 0;
num_bytes = num_bytes - origin;
} else {
num_bytes = file_.ReadAtCurrentPos(reinterpret_cast<char*>(buffer), size);
}
if (num_bytes < 0)
return 0;
bytes_read_ += num_bytes;
return num_bytes;
}
bool FileRStream::isAtEnd() const {
return bytes_read_ == length_;
}
} // namespace paint_preview
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_PAINT_PREVIEW_COMMON_FILE_STREAM_H_
#define COMPONENTS_PAINT_PREVIEW_COMMON_FILE_STREAM_H_
#include "base/files/file.h"
#include "base/macros.h"
#include "third_party/skia/include/core/SkStream.h"
namespace paint_preview {
// An implementation of the SkWStream interface backed by base::File.
class FileWStream : public SkWStream {
public:
// Note: |file| must support writing.
FileWStream(base::File file);
~FileWStream() override;
bool write(const void* buffer, size_t size) override;
void flush() override;
size_t bytesWritten() const override;
// Closes the file (occurs automatically on destruction).
void Close();
private:
base::File file_;
size_t bytes_written_;
DISALLOW_COPY_AND_ASSIGN(FileWStream);
};
// An implementation of the SkWStream interface backed by base::File. Only
// implements the minimal interface and not the Seekable/Rewindable variants.
class FileRStream : public SkStream {
public:
// Note: |file| must support reading. It *cannot* be modified while streaming.
FileRStream(base::File file);
~FileRStream() override;
size_t read(void* buffer, size_t size) override;
bool isAtEnd() const override;
private:
base::File file_;
// Length is fixed at the start of streaming as the file should not be
// modified. This value must be cached as base::File::GetLength() is not a
// const operation.
const size_t length_;
size_t bytes_read_;
DISALLOW_COPY_AND_ASSIGN(FileRStream);
};
} // namespace paint_preview
#endif // COMPONENTS_PAINT_PREVIEW_COMMON_FILE_STREAM_H_
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/paint_preview/common/file_stream.h"
#include "base/files/scoped_temp_dir.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace paint_preview {
// Test reading and writing from a file.
TEST(PaintPreviewFileStreamTest, TestWriteRead) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("test_file");
std::vector<uint8_t> test_data = {0, 1, 2, 3, 4, 5, 8, 9};
base::File write_file(
file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
FileWStream wstream(std::move(write_file));
EXPECT_TRUE(wstream.write(test_data.data(), test_data.size()));
wstream.flush();
wstream.Close();
EXPECT_EQ(wstream.bytesWritten(), test_data.size());
base::File read_file(file_path, base::File::FLAG_OPEN |
base::File::FLAG_READ |
base::File::FLAG_EXCLUSIVE_READ);
FileRStream rstream(std::move(read_file));
EXPECT_FALSE(rstream.isAtEnd());
std::vector<uint8_t> read_data(test_data.size(), 0xFF);
EXPECT_EQ(rstream.read(read_data.data(), read_data.size()), read_data.size());
EXPECT_THAT(read_data,
testing::ElementsAreArray(test_data.begin(), test_data.end()));
EXPECT_TRUE(rstream.isAtEnd());
}
// Test writing failure.
TEST(PaintPreviewFileStreamTest, TestWriteFail) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("test_file");
std::vector<uint8_t> test_data = {0, 1, 2, 3, 4, 5, 8, 9};
base::File write_file(file_path,
base::File::FLAG_OPEN_ALWAYS | base::File::FLAG_READ);
FileWStream wstream(std::move(write_file));
EXPECT_FALSE(wstream.write(test_data.data(), test_data.size()));
}
// Test reading to skip.
TEST(PaintPreviewFileStreamTest, TestSkip) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("test_file");
std::vector<uint8_t> test_data = {0, 1, 2, 3, 4, 5, 8, 9};
base::File write_file(
file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
FileWStream wstream(std::move(write_file));
// Write half the data.
EXPECT_TRUE(wstream.write(test_data.data(), 4));
EXPECT_EQ(wstream.bytesWritten(), 4U);
EXPECT_TRUE(wstream.write(test_data.data() + 4, 4));
EXPECT_EQ(wstream.bytesWritten(), test_data.size());
wstream.Close();
base::File read_file(file_path, base::File::FLAG_OPEN |
base::File::FLAG_READ |
base::File::FLAG_EXCLUSIVE_READ);
FileRStream rstream(std::move(read_file));
EXPECT_FALSE(rstream.isAtEnd());
EXPECT_EQ(rstream.read(nullptr, test_data.size()), test_data.size());
EXPECT_TRUE(rstream.isAtEnd());
}
// Test read and skip.
TEST(PaintPreviewFileStreamTest, TestReadAndSkip) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("test_file");
std::vector<uint8_t> test_data = {0, 1, 2, 3, 4, 5, 8, 9};
base::File write_file(
file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
FileWStream wstream(std::move(write_file));
EXPECT_TRUE(wstream.write(test_data.data(), test_data.size()));
wstream.Close();
base::File read_file(file_path, base::File::FLAG_OPEN |
base::File::FLAG_READ |
base::File::FLAG_EXCLUSIVE_READ);
const size_t kSkipBytes = 3;
FileRStream rstream(std::move(read_file));
EXPECT_FALSE(rstream.isAtEnd());
EXPECT_EQ(rstream.read(nullptr, kSkipBytes), kSkipBytes);
EXPECT_FALSE(rstream.isAtEnd());
EXPECT_EQ(rstream.read(nullptr, kSkipBytes), kSkipBytes);
EXPECT_FALSE(rstream.isAtEnd());
const size_t kReadBytes = test_data.size() - kSkipBytes * 2;
std::vector<uint8_t> read_data(kReadBytes, 0xFF);
EXPECT_EQ(rstream.read(read_data.data(), read_data.size()), kReadBytes);
EXPECT_THAT(read_data,
testing::ElementsAreArray(test_data.begin() + kSkipBytes * 2,
test_data.end()));
EXPECT_TRUE(rstream.isAtEnd());
}
// Test reading failure.
TEST(PaintPreviewFileStreamTest, TestReadFail) {
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
base::FilePath file_path = temp_dir.GetPath().AppendASCII("test_file");
std::vector<uint8_t> test_data = {0, 1, 2, 3, 4, 5, 8, 9};
base::File write_file(
file_path, base::File::FLAG_CREATE_ALWAYS | base::File::FLAG_WRITE);
FileWStream wstream(std::move(write_file));
EXPECT_TRUE(wstream.write(test_data.data(), test_data.size()));
base::File read_file(file_path,
base::File::FLAG_OPEN | base::File::FLAG_WRITE);
FileRStream rstream(std::move(read_file));
EXPECT_FALSE(rstream.isAtEnd());
std::vector<uint8_t> read_data(test_data.size(), 0xFF);
EXPECT_EQ(rstream.read(read_data.data(), read_data.size()), 0U);
std::vector<uint8_t> expected(test_data.size(), 0xFF);
EXPECT_THAT(read_data,
testing::ElementsAreArray(expected.begin(), expected.end()));
}
} // namespace paint_preview
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment