blob: 7b8eb29c9832d54c3445636a7e3a4ac834f91cdf [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 "pagespeed/kernel/image/frame_interface_optimizer.h"
#include <cstdint>
#include "pagespeed/kernel/base/message_handler.h"
#include "pagespeed/kernel/base/string.h"
namespace pagespeed {
namespace image_compression {
// Takes ownership of reader.
MultipleFramePaddingReader::MultipleFramePaddingReader(
MultipleFrameReader* reader) :
MultipleFrameReader(reader->message_handler()),
impl_(reader) {
}
MultipleFramePaddingReader::~MultipleFramePaddingReader() {}
ScanlineStatus MultipleFramePaddingReader::Reset() {
return impl_->Reset();
}
ScanlineStatus MultipleFramePaddingReader::Initialize() {
ScanlineStatus status = impl_->Initialize(image_buffer_, buffer_length_);
if (status.Success()) {
status = impl_->GetImageSpec(&image_spec_);
}
return status;
}
bool MultipleFramePaddingReader::HasMoreFrames() const {
return impl_->HasMoreFrames();
}
bool MultipleFramePaddingReader::HasMoreScanlines() const {
return current_scanline_idx_ < padded_frame_spec_.height;
}
ScanlineStatus MultipleFramePaddingReader::PrepareNextFrame() {
frame_needs_no_padding_ = false;
frame_is_full_height_ = false;
frame_is_full_width_ = false;
// If image_spec_.use_bg_color == false, then we pad the frame with
// the transparent color defined in kTransparent.
static const PixelRgbaChannels kTransparent = {0, 0, 0, kAlphaTransparent};
ScanlineStatus status;
if (impl_->PrepareNextFrame(&status) &&
impl_->GetFrameSpec(&impl_frame_spec_, &status)) {
// Bounds-check the FrameSpec.
impl_frame_spec_.left = image_spec_.TruncateXIndex(impl_frame_spec_.left);
impl_frame_spec_.width =
image_spec_.TruncateXIndex(impl_frame_spec_.left +
impl_frame_spec_.width) -
impl_frame_spec_.left;
padded_frame_spec_ = impl_frame_spec_;
padded_frame_spec_.width = image_spec_.width;
padded_frame_spec_.height = image_spec_.height;
padded_frame_spec_.top = 0;
padded_frame_spec_.left = 0;
bytes_per_pixel_ = GetBytesPerPixel(padded_frame_spec_.pixel_format);
size_px scanline_num_bytes = padded_frame_spec_.width * bytes_per_pixel_;
current_scanline_.reset(new uint8_t[scanline_num_bytes]);
scanline_template_.reset(new uint8_t[scanline_num_bytes]);
uint8_t* template_ptr_end = scanline_template_.get() + scanline_num_bytes;
const void* bg_color = (image_spec_.use_bg_color ?
image_spec_.bg_color : kTransparent);
for (uint8_t* template_ptr = scanline_template_.get();
template_ptr < template_ptr_end;
template_ptr += bytes_per_pixel_) {
memcpy(template_ptr, bg_color, bytes_per_pixel_);
}
current_scanline_idx_ = 0;
// These are guaranteed to be in range because impl_frame_spec_
// was itself bounds-checked above.
size_px foreground_scanline_start_idx = impl_frame_spec_.left;
size_px foreground_scanline_end_idx = (impl_frame_spec_.left +
impl_frame_spec_.width);
foreground_scanline_start_byte_ =
(current_scanline_.get() +
bytes_per_pixel_ * foreground_scanline_start_idx);
frame_is_full_width_ = ((foreground_scanline_start_idx == 0) &&
(foreground_scanline_end_idx == image_spec_.width));
frame_is_full_height_ = ((impl_frame_spec_.top == 0) &&
(impl_frame_spec_.height == image_spec_.height));
frame_needs_no_padding_ = frame_is_full_width_ && frame_is_full_height_;
// Set the background color for all the scanlines to follow. Note
// that since the foreground is rectangular, the same foreground
// pixels will get overwritten in each scanline, while the
// background pixels remain untouched.
memcpy(current_scanline_.get(),
scanline_template_.get(),
scanline_num_bytes);
}
return status;
}
ScanlineStatus MultipleFramePaddingReader::ReadNextScanline(
const void** out_scanline_bytes) {
if (frame_needs_no_padding_) {
// Short-circuit any additional computations.
++current_scanline_idx_;
return impl_->ReadNextScanline(out_scanline_bytes);
}
if (!HasMoreScanlines()) {
return PS_LOGGED_STATUS(PS_LOG_DFATAL, message_handler(),
SCANLINE_STATUS_INVOCATION_ERROR,
FRAME_PADDING_READER,
"no more scanlines in the current frame");
}
const void* impl_scanline = NULL;
ScanlineStatus status;
if (frame_is_full_height_ ||
((current_scanline_idx_ >= impl_frame_spec_.top) &&
(current_scanline_idx_ < (impl_frame_spec_.top +
impl_frame_spec_.height)))) {
// This scanline contains foreground pixels.
// If a full-width row, we can short-circuit the remaining
// computations.
if (frame_is_full_width_) {
++current_scanline_idx_;
return impl_->ReadNextScanline(out_scanline_bytes);
}
// Read the foreground row for use below.
if (!impl_->ReadNextScanline(&impl_scanline, &status)) {
return status;
}
}
if (impl_scanline == NULL) {
// This scanline contains only background pixels.
*out_scanline_bytes = scanline_template_.get();
} else {
// Overwrite the foreground pixels appropriately. Note that the
// background pixels were already set in PrepareNextFrame.
memcpy(foreground_scanline_start_byte_,
impl_scanline,
bytes_per_pixel_ * impl_frame_spec_.width);
*out_scanline_bytes = current_scanline_.get();
}
++current_scanline_idx_;
return status;
}
ScanlineStatus MultipleFramePaddingReader::GetFrameSpec(
FrameSpec* frame_spec) const {
*frame_spec = padded_frame_spec_;
return ScanlineStatus(SCANLINE_STATUS_SUCCESS);
}
ScanlineStatus MultipleFramePaddingReader::GetImageSpec(
ImageSpec* image_spec) const {
ScanlineStatus status = impl_->GetImageSpec(image_spec);
if (status.Success() && !image_spec->Equals(image_spec_)) {
return ScanlineStatus(SCANLINE_STATUS_INTERNAL_ERROR,
FRAME_PADDING_READER,
"ImageSpec changed during image processing");
}
return status;
}
MessageHandler* MultipleFramePaddingReader::message_handler() const {
return impl_->message_handler();
}
ScanlineStatus MultipleFramePaddingReader::set_quirks_mode(
QuirksMode quirks_mode) {
return impl_->set_quirks_mode(quirks_mode);
}
QuirksMode MultipleFramePaddingReader::quirks_mode() const {
return impl_->quirks_mode();
}
} // namespace image_compression
} // namespace pagespeed