blob: af95b3bbfeaaac8c4c7306e00d3f27872ccda5de [file] [log] [blame]
// Copyright 2016 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/loader/image/image_data_decoder.h"
#include <algorithm>
#include "base/debug/trace_event.h"
namespace cobalt {
namespace loader {
namespace image {
namespace {
// The capacity of data buffer.
uint32 kMaxBufferSizeBytes = 32 * 1024L;
} // namespace
ImageDataDecoder::ImageDataDecoder(
render_tree::ResourceProvider* resource_provider)
: resource_provider_(resource_provider), 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 the data_buffer, so no data append needs to be performed.
input_bytes = data + offset;
input_size = size - offset;
offset += input_size;
} else {
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);
size_t undecoded_size = input_size - decoded_size;
if (undecoded_size == 0) {
// Remove all elements from the data_buffer.
data_buffer_.clear();
} else {
data_buffer_.reserve(kMaxBufferSizeBytes);
if (data_buffer_.empty()) {
// data_buffer is empty, so assign the undecoded data to it.
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));
}
}
}
}
bool ImageDataDecoder::FinishWithSuccess() {
TRACE_EVENT0("cobalt::loader::image_decoder",
"ImageDataDecoder::FinishWithSuccess");
FinishInternal();
if (state_ != kDone) {
image_data_.reset();
}
return state_ == kDone;
}
bool ImageDataDecoder::AllocateImageData(const math::Size& size,
bool has_alpha) {
DCHECK(resource_provider_->AlphaFormatSupported(
render_tree::kAlphaFormatOpaque));
DCHECK(resource_provider_->AlphaFormatSupported(
render_tree::kAlphaFormatPremultiplied));
image_data_ = resource_provider_->AllocateImageData(
size, pixel_format(), has_alpha ? render_tree::kAlphaFormatPremultiplied
: render_tree::kAlphaFormatOpaque);
if (!image_data_) {
DLOG(WARNING) << "Failed to allocate image data (" << size.width() << "x"
<< size.height() << ").";
// We want to know in debug if we have problems allocating image data.
// It should never happen.
DCHECK(false);
}
return 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