| // Copyright 2015 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "cobalt/browser/screen_shot_writer.h" |
| |
| #include "base/bind.h" |
| #include "base/file_util.h" |
| #include "cobalt/renderer/test/png_utils/png_encode.h" |
| #include "third_party/skia/include/core/SkBitmap.h" |
| #include "third_party/skia/include/core/SkPixelRef.h" |
| |
| using cobalt::renderer::test::png_utils::EncodeRGBAToBuffer; |
| |
| namespace cobalt { |
| namespace browser { |
| namespace { |
| scoped_array<uint8> WriteRGBAPixelsToPNG(scoped_array<uint8> pixel_data, |
| const math::Size& dimensions, |
| size_t* out_num_bytes) { |
| // Given a chunk of memory formatted as RGBA8 with pitch = width * 4, this |
| // function will wrap that memory in a SkBitmap that does *not* own the |
| // pixels and return that. |
| const int kRGBABytesPerPixel = 4; |
| SkBitmap bitmap; |
| bitmap.installPixels( |
| SkImageInfo::Make(dimensions.width(), dimensions.height(), |
| kRGBA_8888_SkColorType, kUnpremul_SkAlphaType), |
| const_cast<uint8_t*>(pixel_data.get()), |
| dimensions.width() * kRGBABytesPerPixel); |
| |
| // No conversion needed here, simply write out the pixels as is. |
| return EncodeRGBAToBuffer(static_cast<uint8_t*>(bitmap.pixelRef()->pixels()), |
| bitmap.width(), bitmap.height(), |
| static_cast<int>(bitmap.rowBytes()), out_num_bytes); |
| } |
| } // namespace |
| |
| ScreenShotWriter::ScreenShotWriter(renderer::Pipeline* pipeline) |
| : pipeline_(pipeline), |
| screenshot_thread_("Screenshot IO thread") { |
| base::Thread::Options options; |
| options.message_loop_type = MessageLoop::TYPE_IO; |
| screenshot_thread_.StartWithOptions(options); |
| } |
| |
| void ScreenShotWriter::RequestScreenshot(const FilePath& output_path, |
| const base::Closure& complete) { |
| RequestScreenshotToMemory(base::Bind(&ScreenShotWriter::EncodingComplete, |
| base::Unretained(this), output_path, |
| complete)); |
| } |
| |
| void ScreenShotWriter::RequestScreenshotToMemory( |
| const PNGEncodeCompleteCallback& callback) { |
| DCHECK(!callback.is_null()); |
| DCHECK(last_submission_); |
| renderer::Submission submission(last_submission_.value()); |
| submission.time_offset += |
| base::TimeTicks::HighResNow() - last_submission_time_; |
| pipeline_->RasterizeToRGBAPixels( |
| submission, base::Bind(&ScreenShotWriter::RasterizationComplete, |
| base::Unretained(this), callback)); |
| } |
| |
| void ScreenShotWriter::SetLastPipelineSubmission( |
| const renderer::Submission& submission) { |
| DCHECK(submission.render_tree.get()); |
| last_submission_ = submission; |
| last_submission_time_ = base::TimeTicks::HighResNow(); |
| } |
| |
| void ScreenShotWriter::ClearLastPipelineSubmission() { |
| last_submission_ = base::nullopt; |
| } |
| |
| void ScreenShotWriter::RasterizationComplete( |
| const PNGEncodeCompleteCallback& encode_complete_callback, |
| scoped_array<uint8> pixel_data, const math::Size& dimensions) { |
| if (MessageLoop::current() != screenshot_thread_.message_loop()) { |
| screenshot_thread_.message_loop()->PostTask( |
| FROM_HERE, base::Bind(&ScreenShotWriter::RasterizationComplete, |
| base::Unretained(this), encode_complete_callback, |
| base::Passed(&pixel_data), dimensions)); |
| return; |
| } |
| size_t num_bytes; |
| scoped_array<uint8> png_data = |
| WriteRGBAPixelsToPNG(pixel_data.Pass(), dimensions, &num_bytes); |
| |
| encode_complete_callback.Run(png_data.Pass(), num_bytes); |
| } |
| |
| void ScreenShotWriter::EncodingComplete(const FilePath& output_path, |
| const base::Closure& complete_callback, |
| scoped_array<uint8> png_data, |
| size_t num_bytes) { |
| DCHECK_EQ(MessageLoop::current(), screenshot_thread_.message_loop()); |
| // Blocking write to output_path. |
| int bytes_written = |
| file_util::WriteFile(output_path, reinterpret_cast<char*>(png_data.get()), |
| static_cast<int>(num_bytes)); |
| DLOG_IF(ERROR, bytes_written != num_bytes) << "Error writing PNG to file."; |
| |
| // Notify the caller that the screenshot is complete. |
| if (!complete_callback.is_null()) { |
| complete_callback.Run(); |
| } |
| } |
| |
| } // namespace browser |
| } // namespace cobalt |