| 521 | } |
| 522 | |
| 523 | bool Bitmap::Write(const std::filesystem::path& path, |
| 524 | const bool delinearize_colorspace) const { |
| 525 | const std::string utf8_path = PathToUtf8(path); |
| 526 | const auto output = OIIO::ImageOutput::create(utf8_path); |
| 527 | if (!output) { |
| 528 | std::cerr << "Could not create an ImageOutput for " << path |
| 529 | << ", error = " << OIIO::geterror() << "\n"; |
| 530 | return false; |
| 531 | } |
| 532 | |
| 533 | // Create a copy of the metadata to avoid modifying the original. |
| 534 | OIIOMetaData meta_data = *OIIOMetaData::Upcast(meta_data_.get()); |
| 535 | |
| 536 | const uint8_t* output_data_ptr = data_.data(); |
| 537 | std::vector<uint8_t> maybe_linearized_output_data; |
| 538 | if (delinearize_colorspace && linear_colorspace_) { |
| 539 | std::optional<std::string> colorspace = GetMetaData("oiio:ColorSpace"); |
| 540 | if (!colorspace.has_value()) { |
| 541 | // Assume sRGB color space if not specified. |
| 542 | colorspace = "sRGB"; |
| 543 | SetImageSpecColorSpace(meta_data.image_spec, |
| 544 | OIIOFromStdStringView(*colorspace)); |
| 545 | } |
| 546 | |
| 547 | maybe_linearized_output_data = ConvertColorSpace( |
| 548 | data_.data(), width_, height_, channels_, "linear", *colorspace); |
| 549 | output_data_ptr = maybe_linearized_output_data.data(); |
| 550 | } |
| 551 | |
| 552 | if (HasFileExtension(path, ".jpg") || HasFileExtension(path, ".jpeg")) { |
| 553 | if (!GetMetaData("Compression").has_value()) { |
| 554 | // Save JPEG in superb quality by default to reduce compression artifacts. |
| 555 | meta_data.image_spec["Compression"] = "jpeg:100"; |
| 556 | } |
| 557 | } |
| 558 | |
| 559 | if (!output->open(utf8_path, meta_data.image_spec)) { |
| 560 | VLOG(3) << "Could not open " << path << ", error = " << output->geterror() |
| 561 | << "\n"; |
| 562 | return false; |
| 563 | } |
| 564 | |
| 565 | if (!output->write_image(OIIO::TypeDesc::UINT8, output_data_ptr)) { |
| 566 | VLOG(3) << "Could not write pixels to " << path |
| 567 | << ", error = " << output->geterror() << "\n"; |
| 568 | return false; |
| 569 | } |
| 570 | |
| 571 | if (!output->close()) { |
| 572 | VLOG(3) << "Error closing " << path << ", error = " << output->geterror() |
| 573 | << "\n"; |
| 574 | return false; |
| 575 | } |
| 576 | |
| 577 | return true; |
| 578 | } |
| 579 | |
| 580 | void Bitmap::Rescale(const int new_width, |