blob: ad4f3d3f05abd1f2fadd1c3054bafe39f07082e9 [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/webp_image_decoder.h"
#include "base/debug/trace_event.h"
#include "base/logging.h"
namespace cobalt {
namespace loader {
namespace image {
WEBPImageDecoder::WEBPImageDecoder(
render_tree::ResourceProvider* resource_provider)
: ImageDataDecoder(resource_provider), internal_decoder_(NULL) {
TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::WEBPImageDecoder()");
// Initialize the configuration as empty.
WebPInitDecoderConfig(&config_);
// Skip the in-loop filtering.
config_.options.bypass_filtering = 1;
// Use faster pointwise upsampler.
config_.options.no_fancy_upsampling = 1;
// Don't use multi-threaded decoding.
config_.options.use_threads = 0;
// Discard enhancement layer.
config_.options.no_enhancement = 1;
}
WEBPImageDecoder::~WEBPImageDecoder() {
TRACE_EVENT0("cobalt::loader::image",
"WEBPImageDecoder::~WEBPImageDecoder()");
DeleteInternalDecoder();
}
size_t WEBPImageDecoder::DecodeChunkInternal(const uint8* data,
size_t input_byte) {
TRACE_EVENT0("cobalt::loader::image",
"WEBPImageDecoder::DecodeChunkInternal()");
const uint8* next_input_byte = data;
size_t bytes_in_buffer = input_byte;
if (state() == kWaitingForHeader) {
bool has_alpha = false;
if (!ReadHeader(next_input_byte, bytes_in_buffer, &has_alpha)) {
return 0;
}
if (!CreateInternalDecoder(has_alpha)) {
return 0;
}
set_state(kReadLines);
}
if (state() == kReadLines) {
// Copies and decodes the next available data. Returns VP8_STATUS_OK when
// the image is successfully decoded. Returns VP8_STATUS_SUSPENDED when more
// data is expected. Returns error in other cases.
VP8StatusCode status =
WebPIAppend(internal_decoder_, next_input_byte, bytes_in_buffer);
if (status == VP8_STATUS_OK) {
DCHECK(image_data());
DCHECK(config_.output.u.RGBA.rgba);
memcpy(image_data()->GetMemory(), config_.output.u.RGBA.rgba,
config_.output.u.RGBA.size);
DeleteInternalDecoder();
set_state(kDone);
} else if (status != VP8_STATUS_SUSPENDED) {
DLOG(ERROR) << "WebPIAppend error, status code: " << status;
DeleteInternalDecoder();
set_state(kError);
}
}
return input_byte;
}
bool WEBPImageDecoder::ReadHeader(const uint8* data, size_t size,
bool* has_alpha) {
TRACE_EVENT0("cobalt::loader::image", "WEBPImageDecoder::ReadHeader()");
// Retrieve features from the bitstream. The *features structure is filled
// with information gathered from the bitstream.
// Returns VP8_STATUS_OK when the features are successfully retrieved. Returns
// VP8_STATUS_NOT_ENOUGH_DATA when more data is needed to retrieve the
// features from headers. Returns error in other cases.
VP8StatusCode status = WebPGetFeatures(data, size, &config_.input);
if (status == VP8_STATUS_OK) {
int width = config_.input.width;
int height = config_.input.height;
*has_alpha = !!config_.input.has_alpha;
return AllocateImageData(math::Size(width, height), *has_alpha);
} else if (status == VP8_STATUS_NOT_ENOUGH_DATA) {
// Data is not enough for decoding the header.
return false;
} else {
DLOG(ERROR) << "WebPGetFeatures error, status code: " << status;
set_state(kError);
return false;
}
}
bool WEBPImageDecoder::CreateInternalDecoder(bool has_alpha) {
TRACE_EVENT0("cobalt::loader::image",
"WEBPImageDecoder::CreateInternalDecoder()");
config_.output.colorspace = has_alpha ? MODE_rgbA : MODE_RGBA;
config_.output.u.RGBA.stride = image_data()->GetDescriptor().pitch_in_bytes;
config_.output.u.RGBA.size =
static_cast<size_t>(config_.output.u.RGBA.stride *
image_data()->GetDescriptor().size.height());
// We don't use image buffer as the decoding buffer because libwebp will read
// from it while we assume that our image buffer is write only.
config_.output.is_external_memory = 0;
// Instantiate a new incremental decoder object with the requested
// configuration.
internal_decoder_ = WebPIDecode(NULL, 0, &config_);
if (internal_decoder_ == NULL) {
DLOG(WARNING) << "Create internal WEBP decoder failed.";
set_state(kError);
return false;
}
return true;
}
void WEBPImageDecoder::DeleteInternalDecoder() {
TRACE_EVENT0("cobalt::loader::image",
"WEBPImageDecoder::DeleteInternalDecoder()");
if (internal_decoder_) {
// Deletes the WebPIDecoder object and associated memory. Must always be
// called if WebPIDecode succeeded.
WebPIDelete(internal_decoder_);
internal_decoder_ = NULL;
WebPFreeDecBuffer(&config_.output);
}
}
} // namespace image
} // namespace loader
} // namespace cobalt