blob: 0a19fdff7681882d4ee4be39aa4328e547086593 [file] [log] [blame]
// 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