| // Copyright 2016 The Cobalt Authors. 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/loader/image/image_data_decoder.h" |
| |
| #include <algorithm> |
| #include <memory> |
| |
| #include "base/trace_event/trace_event.h" |
| |
| namespace cobalt { |
| namespace loader { |
| namespace image { |
| |
| namespace { |
| // Sanity check max size of data buffer. |
| uint32 kMaxBufferSizeBytes = 4 * 1024 * 1024L; |
| } // namespace |
| |
| ImageDataDecoder::ImageDataDecoder( |
| render_tree::ResourceProvider* resource_provider, |
| const base::DebuggerHooks& debugger_hooks) |
| : resource_provider_(resource_provider), |
| debugger_hooks_(debugger_hooks), |
| state_(kWaitingForHeader) { |
| CalculatePixelFormat(); |
| } |
| |
| void ImageDataDecoder::DecodeChunk(const uint8* data, size_t size) { |
| TRACE_EVENT0("cobalt::loader::image_decoder", |
| "ImageDataDecoder::DecodeChunk"); |
| size_t offset = 0; |
| while (offset < size) { |
| if (state_ == kError) { |
| // Previous chunk causes an error, so there is nothing to do in here. |
| return; |
| } |
| |
| const uint8* input_bytes; |
| size_t input_size; |
| |
| if (data_buffer_.empty()) { |
| // Nothing in |data_buffer_|, so no data append needs to be performed. |
| input_bytes = data + offset; |
| input_size = size - offset; |
| offset += input_size; |
| } else { |
| DCHECK_GE(kMaxBufferSizeBytes, data_buffer_.size()); |
| size_t fill_buffer_size = |
| std::min(kMaxBufferSizeBytes - data_buffer_.size(), size - offset); |
| |
| // Append new data to data_buffer |
| data_buffer_.insert(data_buffer_.end(), data + offset, |
| data + offset + fill_buffer_size); |
| |
| input_bytes = &data_buffer_[0]; |
| input_size = data_buffer_.size(); |
| offset += fill_buffer_size; |
| } |
| |
| size_t decoded_size = DecodeChunkInternal(input_bytes, input_size); |
| if (decoded_size == 0 && offset < size) { |
| LOG(ERROR) << "Unable to make progress decoding image."; |
| state_ = kError; |
| return; |
| } |
| |
| size_t undecoded_size = input_size - decoded_size; |
| if (undecoded_size == 0) { |
| // Remove all elements from the data_buffer. |
| data_buffer_.clear(); |
| } else { |
| if (data_buffer_.empty()) { |
| if (undecoded_size > kMaxBufferSizeBytes) { |
| LOG(ERROR) << "Max buffer size too small: " << undecoded_size |
| << "bytes required!"; |
| state_ = kError; |
| return; |
| } |
| |
| // |data_buffer_| is empty, so assign the undecoded data to it. |
| data_buffer_.reserve(undecoded_size); |
| data_buffer_.assign(data + offset - undecoded_size, data + offset); |
| } else if (decoded_size != 0) { |
| // |data_buffer_| is not empty, so erase the decoded data from it. |
| data_buffer_.erase( |
| data_buffer_.begin(), |
| data_buffer_.begin() + static_cast<ptrdiff_t>(decoded_size)); |
| } |
| } |
| } |
| } |
| |
| scoped_refptr<Image> ImageDataDecoder::FinishAndMaybeReturnImage() { |
| TRACE_EVENT0("cobalt::loader::image_decoder", |
| "ImageDataDecoder::FinishAndMaybeReturnImage"); |
| |
| return FinishInternal(); |
| } |
| |
| std::unique_ptr<render_tree::ImageData> ImageDataDecoder::AllocateImageData( |
| const math::Size& size, bool has_alpha) { |
| DCHECK(resource_provider_->AlphaFormatSupported( |
| render_tree::kAlphaFormatOpaque)); |
| DCHECK(resource_provider_->AlphaFormatSupported( |
| render_tree::kAlphaFormatPremultiplied)); |
| auto image_data = resource_provider_->AllocateImageData( |
| size, pixel_format(), |
| has_alpha ? render_tree::kAlphaFormatPremultiplied |
| : render_tree::kAlphaFormatOpaque); |
| DLOG_IF(ERROR, !image_data) << "Failed to allocate image data (" |
| << size.width() << "x" << size.height() << ")."; |
| return image_data; |
| } |
| |
| scoped_refptr<Image> ImageDataDecoder::CreateStaticImage( |
| std::unique_ptr<render_tree::ImageData> image_data) { |
| DCHECK(image_data); |
| return new StaticImage( |
| resource_provider()->CreateImage(std::move(image_data))); |
| } |
| |
| void ImageDataDecoder::CalculatePixelFormat() { |
| pixel_format_ = render_tree::kPixelFormatRGBA8; |
| if (!resource_provider_->PixelFormatSupported(pixel_format_)) { |
| pixel_format_ = render_tree::kPixelFormatBGRA8; |
| } |
| |
| DCHECK(resource_provider_->PixelFormatSupported(pixel_format_)); |
| } |
| |
| } // namespace image |
| } // namespace loader |
| } // namespace cobalt |