Improve File error handling. Massive effects...

API is somewhat stolen from Rust std::fs::File. Static factories didn't work out so well, though. Move semantics made my head explode.
TODO: Still a lot of places where errors aren't handled, but it's an improvement...
This commit is contained in:
Jared Boone 2016-05-16 14:01:44 -07:00
parent d905c446bf
commit 682a1706a3
18 changed files with 328 additions and 235 deletions

View File

@ -126,25 +126,17 @@ static std::string true_heading(const TrueHeading value) {
} /* namespace format */
} /* namespace ais */
AISLogger::AISLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void AISLogger::on_packet(const ais::Packet& packet) {
// TODO: Unstuff here, not in baseband!
if( log_file.is_open() ) {
std::string entry;
entry.reserve((packet.length() + 3) / 4);
std::string entry;
entry.reserve((packet.length() + 3) / 4);
for(size_t i=0; i<packet.length(); i+=4) {
const auto nibble = packet.read(i, 4);
entry += (nibble >= 10) ? ('W' + nibble) : ('0' + nibble);
}
log_file.write_entry(packet.received_at(), entry);
for(size_t i=0; i<packet.length(); i+=4) {
const auto nibble = packet.read(i, 4);
entry += (nibble >= 10) ? ('W' + nibble) : ('0' + nibble);
}
log_file.write_entry(packet.received_at(), entry);
}
void AISRecentEntry::update(const ais::Packet& packet) {
@ -332,7 +324,10 @@ AISAppView::AISAppView(NavigationView&) {
this->on_show_list();
};
logger = std::make_unique<AISLogger>("ais.txt");
logger = std::make_unique<AISLogger>();
if( logger ) {
logger->append("ais.txt");
}
}
AISAppView::~AISAppView() {

View File

@ -92,8 +92,10 @@ using AISRecentEntries = RecentEntries<ais::Packet, AISRecentEntry>;
class AISLogger {
public:
AISLogger(const std::string& file_path);
Optional<File::Error> append(const std::string& filename) {
return log_file.append(filename);
}
void on_packet(const ais::Packet& packet);
private:

View File

@ -98,13 +98,8 @@ CaptureThread::~CaptureThread() {
}
}
Optional<std::string> CaptureThread::error() const {
const auto error = writer->error();
if( error.is_valid() ) {
return { error.value().what() };
} else {
return { };
}
const Optional<File::Error>& CaptureThread::error() const {
return last_error;
}
void CaptureThread::check_fifo_isr() {
@ -118,14 +113,15 @@ void CaptureThread::check_fifo_isr() {
}
}
msg_t CaptureThread::run() {
Optional<File::Error> CaptureThread::run() {
StreamOutput stream { &config };
while( !chThdShouldTerminate() ) {
if( stream.available() ) {
auto buffer = stream.get_buffer();
if( !writer->write(buffer->data(), buffer->size()) ) {
return false;
auto write_result = writer->write(buffer->data(), buffer->size());
if( write_result.is_error() ) {
return write_result.error();
}
stream.release_buffer(buffer);
} else {
@ -133,5 +129,5 @@ msg_t CaptureThread::run() {
}
}
return true;
return { };
}

View File

@ -35,8 +35,7 @@
class Writer {
public:
virtual bool write(const void* const buffer, const size_t bytes) = 0;
virtual Optional<std::filesystem::filesystem_error> error() const = 0;
virtual File::Result<size_t> write(const void* const buffer, const size_t bytes) = 0;
virtual ~Writer() = default;
};
@ -53,21 +52,23 @@ public:
return config;
}
Optional<std::string> error() const;
const Optional<File::Error>& error() const;
static void check_fifo_isr();
private:
CaptureConfig config;
std::unique_ptr<Writer> writer;
Optional<File::Error> last_error;
static Thread* thread;
static msg_t static_fn(void* arg) {
auto obj = static_cast<CaptureThread*>(arg);
return obj->run();
obj->last_error = obj->run();
return 0;
}
msg_t run();
Optional<File::Error> run();
};
#endif/*__CAPTURE_THREAD_H__*/

View File

@ -57,17 +57,9 @@ std::string commodity_type(CommodityType value) {
} /* namespace ert */
ERTLogger::ERTLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void ERTLogger::on_packet(const ert::Packet& packet) {
if( log_file.is_open() ) {
const auto formatted = packet.symbols_formatted();
log_file.write_entry(packet.received_at(), formatted.data + "/" + formatted.errors);
}
const auto formatted = packet.symbols_formatted();
log_file.write_entry(packet.received_at(), formatted.data + "/" + formatted.errors);
}
const ERTRecentEntry::Key ERTRecentEntry::invalid_key { };
@ -148,7 +140,10 @@ ERTAppView::ERTAppView(NavigationView&) {
.decimation_factor = 1,
});
logger = std::make_unique<ERTLogger>("ert.txt");
logger = std::make_unique<ERTLogger>();
if( logger ) {
logger->append("ert.txt");
}
}
ERTAppView::~ERTAppView() {

View File

@ -87,8 +87,10 @@ struct ERTRecentEntry {
class ERTLogger {
public:
ERTLogger(const std::string& file_path);
Optional<File::Error> append(const std::string& filename) {
return log_file.append(filename);
}
void on_packet(const ert::Packet& packet);
private:

View File

@ -28,99 +28,93 @@ static_assert(sizeof(FIL::err) == 1, "FatFs FIL::err size not expected.");
#define FR_DISK_FULL (0x100)
#define FR_EOF (0x101)
#define FR_BAD_SEEK (0x102)
#define FR_UNEXPECTED (0x103)
File::File(
const std::string& filename,
openmode mode
) : err { FR_OK }
{
BYTE fatfs_mode = 0;
if( mode & openmode::in ) {
fatfs_mode |= FA_READ;
}
if( mode & openmode::out ) {
fatfs_mode |= FA_WRITE;
}
if( mode & openmode::trunc ) {
fatfs_mode |= FA_CREATE_ALWAYS;
}
if( mode & openmode::ate ) {
fatfs_mode |= FA_OPEN_ALWAYS;
}
err = f_open(&f, filename.c_str(), fatfs_mode);
if( err == FR_OK ) {
if( mode & openmode::ate ) {
err = f_lseek(&f, f_size(&f));
if( err != FR_OK ) {
Optional<File::Error> File::open_fatfs(const std::string& filename, BYTE mode) {
auto result = f_open(&f, filename.c_str(), mode);
if( result == FR_OK ) {
if( mode & FA_OPEN_ALWAYS ) {
const auto result = f_lseek(&f, f_size(&f));
if( result != FR_OK ) {
f_close(&f);
}
}
}
if( result == FR_OK ) {
return { };
} else {
return { result };
}
}
Optional<File::Error> File::open(const std::string& filename) {
return open_fatfs(filename, FA_READ);
}
Optional<File::Error> File::append(const std::string& filename) {
return open_fatfs(filename, FA_WRITE | FA_OPEN_ALWAYS);
}
Optional<File::Error> File::create(const std::string& filename) {
return open_fatfs(filename, FA_WRITE | FA_CREATE_ALWAYS);
}
File::~File() {
f_close(&f);
}
bool File::read(void* const data, const size_t bytes_to_read) {
if( err != FR_OK ) {
return false;
}
File::Result<size_t> File::read(void* const data, const size_t bytes_to_read) {
UINT bytes_read = 0;
err = f_read(&f, data, bytes_to_read, &bytes_read);
if( bytes_read != bytes_to_read ) {
err = FR_EOF;
const auto result = f_read(&f, data, bytes_to_read, &bytes_read);
if( result == FR_OK ) {
return { static_cast<size_t>(bytes_read) };
} else {
return { static_cast<Error>(result) };
}
return (err == FR_OK);
}
bool File::write(const void* const data, const size_t bytes_to_write) {
if( err != FR_OK ) {
return false;
}
File::Result<size_t> File::write(const void* const data, const size_t bytes_to_write) {
UINT bytes_written = 0;
err = f_write(&f, data, bytes_to_write, &bytes_written);
if( bytes_written != bytes_to_write ) {
err = FR_DISK_FULL;
const auto result = f_write(&f, data, bytes_to_write, &bytes_written);
if( result == FR_OK ) {
return { static_cast<size_t>(bytes_written) };
} else {
return { static_cast<Error>(result) };
}
return (err == FR_OK);
}
uint64_t File::seek(const uint64_t new_position) {
if( err != FR_OK ) {
return false;
}
File::Result<uint64_t> File::seek(const uint64_t new_position) {
/* NOTE: Returns *old* position, not new position */
const auto old_position = f_tell(&f);
err = f_lseek(&f, new_position);
if( err != FR_OK ) {
f_close(&f);
const auto result = f_lseek(&f, new_position);
if( result != FR_OK ) {
return { static_cast<Error>(result) };
}
if( f_tell(&f) != new_position ) {
err = FR_BAD_SEEK;
f_close(&f);
return { static_cast<Error>(FR_BAD_SEEK) };
}
return old_position;
return { static_cast<uint64_t>(old_position) };
}
bool File::puts(const std::string& string) {
File::Result<size_t> File::puts(const std::string& string) {
const auto result = f_puts(string.c_str(), &f);
if( result != (int)string.size() ) {
err = FR_DISK_FULL;
if( result >= 0 ) {
return { static_cast<size_t>(result) };
} else if( result == EOF ) {
return { static_cast<Error>(f_error(&f)) };
} else {
return { static_cast<Error>(FR_UNEXPECTED) };
}
return (result >= 0);
}
bool File::sync() {
if( err != FR_OK ) {
return false;
Optional<File::Error> File::sync() {
const auto result = f_sync(&f);
if( result == FR_OK ) {
return { };
} else {
return { result };
}
err = f_sync(&f);
return (err == FR_OK);
}
static std::string find_last_file_matching_pattern(const std::string& pattern) {
@ -181,7 +175,7 @@ namespace filesystem {
std::string filesystem_error::what() const {
switch(err) {
case FR_OK: return "";
case FR_OK: return "ok";
case FR_DISK_ERR: return "disk error";
case FR_INT_ERR: return "insanity detected";
case FR_NOT_READY: return "not ready";
@ -204,6 +198,7 @@ std::string filesystem_error::what() const {
case FR_EOF: return "end of file";
case FR_DISK_FULL: return "disk full";
case FR_BAD_SEEK: return "bad seek";
case FR_UNEXPECTED: return "unexpected";
default: return "unknown";
}
}

View File

@ -24,6 +24,8 @@
#include "ff.h"
#include "optional.hpp"
#include <cstddef>
#include <cstdint>
#include <string>
@ -37,9 +39,27 @@ namespace std {
namespace filesystem {
struct filesystem_error {
const uint32_t err;
constexpr filesystem_error(
) : err { FR_OK }
{
}
constexpr filesystem_error(
FRESULT fatfs_error
) : err { fatfs_error }
{
}
constexpr filesystem_error(
unsigned int other_error
) : err { other_error }
{
}
std::string what() const;
private:
uint32_t err;
};
using path = std::string;
@ -109,51 +129,89 @@ space_info space(const path& p);
class File {
public:
enum openmode {
app = 0x100,
binary = 0x200,
in = FA_READ,
out = FA_WRITE,
trunc = FA_CREATE_ALWAYS,
ate = FA_OPEN_ALWAYS,
using Error = std::filesystem::filesystem_error;
template<typename T>
struct Result {
enum class Type {
Success,
Error,
} type;
union {
T value_;
Error error_;
};
bool is_ok() const {
return type == Type::Success;
}
bool is_error() const {
return type == Type::Error;
}
const T& value() {
return value_;
}
Error error() const {
return error_;
}
Result() = delete;
constexpr Result(
T value
) : type { Type::Success },
value_ { value }
{
}
constexpr Result(
Error error
) : type { Type::Error },
error_ { error }
{
}
~Result() {
if( type == Type::Success ) {
value_.~T();
}
}
};
File(const std::string& filename, openmode mode);
File() { };
~File();
bool is_open() const {
return err == FR_OK;
}
/* Prevent copies */
File(const File&) = delete;
File& operator=(const File&) = delete;
bool bad() const {
return err != FR_OK;
}
// TODO: Return Result<>.
Optional<Error> open(const std::string& filename);
Optional<Error> append(const std::string& filename);
Optional<Error> create(const std::string& filename);
std::filesystem::filesystem_error error() const {
return { err };
}
Result<size_t> read(void* const data, const size_t bytes_to_read);
Result<size_t> write(const void* const data, const size_t bytes_to_write);
bool read(void* const data, const size_t bytes_to_read);
bool write(const void* const data, const size_t bytes_to_write);
uint64_t seek(const uint64_t new_position);
Result<uint64_t> seek(const uint64_t new_position);
template<size_t N>
bool write(const std::array<uint8_t, N>& data) {
Result<size_t> write(const std::array<uint8_t, N>& data) {
return write(data.data(), N);
}
bool puts(const std::string& string);
Result<size_t> puts(const std::string& string);
bool sync();
// TODO: Return Result<>.
Optional<Error> sync();
private:
FIL f;
uint32_t err;
Optional<Error> open_fatfs(const std::string& filename, BYTE mode);
};
inline constexpr File::openmode operator|(File::openmode a, File::openmode b) {
return File::openmode(static_cast<int>(a) | static_cast<int>(b));
}
#endif/*__FILE_H__*/

View File

@ -23,21 +23,15 @@
#include "string_format.hpp"
LogFile::LogFile(
const std::string& file_path
) : file { file_path, File::openmode::out | File::openmode::ate }
{
}
bool LogFile::is_open() const {
return file.is_open();
}
bool LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) {
File::Result<size_t> LogFile::write_entry(const rtc::RTC& datetime, const std::string& entry) {
std::string timestamp = to_string_timestamp(datetime);
return write(timestamp + " " + entry + "\r\n");
}
bool LogFile::write(const std::string& message) {
return file.puts(message) && file.sync();
File::Result<size_t> LogFile::write(const std::string& message) {
auto puts_result = file.puts(message);
if( puts_result.is_ok() ) {
file.sync();
}
return puts_result;
}

View File

@ -31,16 +31,16 @@ using namespace lpc43xx;
class LogFile {
public:
LogFile(const std::string& file_path);
Optional<File::Error> append(const std::string& filename) {
return file.append(filename);
}
bool is_open() const;
bool write_entry(const rtc::RTC& datetime, const std::string& entry);
File::Result<size_t> write_entry(const rtc::RTC& datetime, const std::string& entry);
private:
File file;
bool write(const std::string& message);
File::Result<size_t> write(const std::string& message);
};
#endif/*__LOG_FILE_H__*/

View File

@ -51,22 +51,14 @@ std::string temperature(Temperature temperature) {
} /* namespace tpms */
TPMSLogger::TPMSLogger(
const std::string& file_path
) : log_file { file_path }
{
}
void TPMSLogger::on_packet(const tpms::Packet& packet, const uint32_t target_frequency) {
const auto hex_formatted = packet.symbols_formatted();
if( log_file.is_open() ) {
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto tuning_frequency_str = to_string_dec_uint(target_frequency, 10);
std::string entry = tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.data + "/" + hex_formatted.errors;
log_file.write_entry(packet.received_at(), entry);
}
std::string entry = tuning_frequency_str + " FSK 38.4 19.2 " + hex_formatted.data + "/" + hex_formatted.errors;
log_file.write_entry(packet.received_at(), entry);
}
const TPMSRecentEntry::Key TPMSRecentEntry::invalid_key = { tpms::Reading::Type::None, 0 };
@ -165,7 +157,10 @@ TPMSAppView::TPMSAppView(NavigationView&) {
.decimation_factor = 1,
});
logger = std::make_unique<TPMSLogger>("tpms.txt");
logger = std::make_unique<TPMSLogger>();
if( logger ) {
logger->append("tpms.txt");
}
}
TPMSAppView::~TPMSAppView() {

View File

@ -72,7 +72,9 @@ using TPMSRecentEntries = RecentEntries<tpms::Reading, TPMSRecentEntry>;
class TPMSLogger {
public:
TPMSLogger(const std::string& file_path);
Optional<File::Error> append(const std::string& filename) {
return log_file.append(filename);
}
void on_packet(const tpms::Packet& packet, const uint32_t target_frequency);

View File

@ -86,7 +86,11 @@ void SystemStatusView::on_camera() {
return;
}
PNGWriter png { filename_stem + ".PNG" };
PNGWriter png;
auto create_error = png.create(filename_stem + ".PNG");
if( create_error.is_valid() ) {
return;
}
for(int i=0; i<320; i++) {
std::array<ColorRGB888, 240> row;

View File

@ -34,26 +34,23 @@ using namespace portapack;
class FileWriter : public Writer {
public:
FileWriter(
const std::string& filename
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc }
{
FileWriter() = default;
FileWriter(const FileWriter&) = delete;
FileWriter& operator=(const FileWriter&) = delete;
FileWriter(FileWriter&& file) = delete;
FileWriter& operator=(FileWriter&&) = delete;
Optional<File::Error> create(const std::string& filename) {
return file.create(filename);
}
Optional<std::filesystem::filesystem_error> error() const override {
if( file.bad() ) {
return { file.error() };
} else {
return { };
File::Result<size_t> write(const void* const buffer, const size_t bytes) override {
auto write_result = file.write(buffer, bytes) ;
if( write_result.is_ok() ) {
bytes_written += write_result.value();
}
}
bool write(const void* const buffer, const size_t bytes) override {
const auto success = file.write(buffer, bytes) ;
if( success ) {
bytes_written += bytes;
}
return success;
return write_result;
}
protected:
@ -66,18 +63,33 @@ using RawFileWriter = FileWriter;
class WAVFileWriter : public FileWriter {
public:
WAVFileWriter(
const std::string& filename,
size_t sampling_rate
) : RawFileWriter { filename },
header { sampling_rate }
) : header { sampling_rate }
{
update_header();
}
WAVFileWriter(const WAVFileWriter&) = delete;
WAVFileWriter& operator=(const WAVFileWriter&) = delete;
WAVFileWriter(WAVFileWriter&&) = delete;
WAVFileWriter& operator=(WAVFileWriter&&) = delete;
~WAVFileWriter() {
update_header();
}
Optional<File::Error> create(
const std::string& filename
) {
const auto create_error = FileWriter::create(filename);
if( create_error.is_valid() ) {
return create_error;
} else {
update_header();
return { };
}
}
private:
struct fmt_pcm_t {
constexpr fmt_pcm_t(
@ -132,7 +144,11 @@ private:
void update_header() {
header.set_data_size(bytes_written);
const auto old_position = file.seek(0);
auto seek_result = file.seek(0);
if( seek_result.is_error() ) {
return;
}
const auto old_position = seek_result.value();
file.write(&header, sizeof(header));
file.seek(old_position);
}
@ -206,17 +222,39 @@ void RecordView::start() {
std::unique_ptr<Writer> writer;
switch(file_type) {
case FileType::WAV:
writer = std::make_unique<WAVFileWriter>(
filename_stem + ".WAV",
sampling_rate
);
{
auto p = std::make_unique<WAVFileWriter>(
sampling_rate
);
auto create_error = p->create(
filename_stem + ".WAV"
);
if( create_error.is_valid() ) {
report_error(create_error.value().what());
} else {
writer = std::move(p);
}
}
break;
case FileType::RawS16:
write_metadata_file(filename_stem + ".TXT");
writer = std::make_unique<RawFileWriter>(
filename_stem + ".C16"
);
{
const auto metadata_file_error = write_metadata_file(filename_stem + ".TXT");
if( metadata_file_error.is_valid() ) {
report_error(metadata_file_error.value().what());
return;
}
auto p = std::make_unique<RawFileWriter>();
auto create_error = p->create(
filename_stem + ".C16"
);
if( create_error.is_valid() ) {
report_error(create_error.value().what());
} else {
writer = std::move(p);
}
}
break;
default:
@ -224,19 +262,12 @@ void RecordView::start() {
};
if( writer ) {
const auto error = writer->error();
if( error.is_valid() ) {
report_error(error.value().what());
} else {
text_record_filename.set(filename_stem);
button_record.set_bitmap(&bitmap_stop);
capture_thread = std::make_unique<CaptureThread>(
std::move(writer),
write_size, buffer_count
);
}
} else {
report_error("file type");
text_record_filename.set(filename_stem);
button_record.set_bitmap(&bitmap_stop);
capture_thread = std::make_unique<CaptureThread>(
std::move(writer),
write_size, buffer_count
);
}
}
@ -247,10 +278,22 @@ void RecordView::stop() {
}
}
void RecordView::write_metadata_file(const std::string& filename) {
File file { filename, File::openmode::out | File::openmode::trunc };
file.puts("sample_rate=" + to_string_dec_uint(sampling_rate) + "\n");
file.puts("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency()) + "\n");
Optional<File::Error> RecordView::write_metadata_file(const std::string& filename) {
File file;
const auto create_error = file.create(filename);
if( create_error.is_valid() ) {
return create_error;
} else {
const auto puts_result1 = file.puts("sample_rate=" + to_string_dec_uint(sampling_rate) + "\n");
if( puts_result1.is_error() ) {
return { puts_result1.error() };
}
const auto puts_result2 = file.puts("center_frequency=" + to_string_dec_uint(receiver_model.tuning_frequency()) + "\n");
if( puts_result2.is_error() ) {
return { puts_result2.error() };
}
return { };
}
}
void RecordView::on_tick_second() {
@ -258,7 +301,7 @@ void RecordView::on_tick_second() {
const auto error = capture_thread->error();
if( error.is_valid() ) {
stop();
report_error(error.value());
report_error(error.value().what());
}
const auto dropped_percent = std::min(99U, capture_thread->state().dropped_percent());
const auto s = to_string_dec_uint(dropped_percent, 2, ' ') + "\%";

View File

@ -69,7 +69,7 @@ public:
private:
void toggle();
void write_metadata_file(const std::string& filename);
Optional<File::Error> write_metadata_file(const std::string& filename);
void on_tick_second();

View File

@ -134,22 +134,24 @@ private:
return Result::FailHeap;
}
File file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc };
if( !file.is_open() ) {
File file;
auto file_create_error = file.create(filename);
if( file_create_error.is_valid() ) {
return Result::FailFileOpenWrite;
}
lfsr_word_t v = 1;
const halrtcnt_t test_start = halGetCounterValue();
while( !chThdShouldTerminate() && file.is_open() && (_stats.write_bytes < bytes_to_write) ) {
while( !chThdShouldTerminate() && (_stats.write_bytes < bytes_to_write) ) {
lfsr_fill(v,
reinterpret_cast<lfsr_word_t*>(buffer->data()),
sizeof(*buffer.get()) / sizeof(lfsr_word_t)
);
const halrtcnt_t write_start = halGetCounterValue();
if( !file.write(buffer->data(), buffer->size()) ) {
const auto result_write = file.write(buffer->data(), buffer->size());
if( result_write.is_error() ) {
break;
}
const halrtcnt_t write_end = halGetCounterValue();
@ -179,17 +181,19 @@ private:
return Result::FailHeap;
}
File file { filename, File::openmode::in | File::openmode::binary };
if( !file.is_open() ) {
File file;
auto file_open_error = file.open(filename);
if( file_open_error.is_valid() ) {
return Result::FailFileOpenRead;
}
lfsr_word_t v = 1;
const halrtcnt_t test_start = halGetCounterValue();
while( !chThdShouldTerminate() && file.is_open() && (_stats.read_bytes < bytes_to_read) ) {
while( !chThdShouldTerminate() && (_stats.read_bytes < bytes_to_read) ) {
const halrtcnt_t read_start = halGetCounterValue();
if( !file.read(buffer->data(), buffer->size()) ) {
const auto result_read = file.read(buffer->data(), buffer->size());
if( result_read.is_error() ) {
break;
}
const halrtcnt_t read_end = halGetCounterValue();

View File

@ -49,10 +49,14 @@ static constexpr std::array<uint8_t, 12> png_iend { {
0xae, 0x42, 0x60, 0x82, // CRC
} };
PNGWriter::PNGWriter(
Optional<File::Error> PNGWriter::create(
const std::string& filename
) : file { filename, File::openmode::out | File::openmode::binary | File::openmode::trunc }
{
) {
const auto create_error = file.create(filename);
if( create_error.is_valid() ) {
return create_error;
}
file.write(png_file_header);
file.write(png_ihdr_screen_capture);
@ -63,6 +67,8 @@ PNGWriter::PNGWriter(
constexpr std::array<uint8_t, 2> zlib_header { 0x78, 0x01 }; // Zlib CM, CINFO, FLG.
write_chunk_content(zlib_header);
return { };
}
PNGWriter::~PNGWriter() {

View File

@ -33,9 +33,10 @@
class PNGWriter {
public:
explicit PNGWriter(const std::string& filename);
~PNGWriter();
Optional<File::Error> create(const std::string& filename);
void write_scanline(const std::array<ui::ColorRGB888, 240>& scanline);
private: