| /* |
| * Copyright (C) 1999 Antti Koivisto (koivisto@kde.org) |
| * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Library General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Library General Public License for more details. |
| * |
| * You should have received a copy of the GNU Library General Public License |
| * along with this library; see the file COPYING.LIB. If not, write to |
| * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| * Boston, MA 02110-1301, USA. |
| * |
| */ |
| |
| #include "third_party/blink/renderer/core/style/fill_layer.h" |
| |
| #include "third_party/blink/renderer/core/layout/layout_object.h" |
| #include "third_party/blink/renderer/core/style/data_equivalency.h" |
| |
| namespace blink { |
| |
| struct SameSizeAsFillLayer { |
| FillLayer* next_; |
| |
| Persistent<StyleImage> image_; |
| |
| Length position_x_; |
| Length position_y_; |
| |
| LengthSize size_length_; |
| |
| unsigned bitfields1_; |
| unsigned bitfields2_; |
| }; |
| |
| static_assert(sizeof(FillLayer) == sizeof(SameSizeAsFillLayer), |
| "FillLayer should stay small"); |
| |
| FillLayer::FillLayer(EFillLayerType type, bool use_initial_values) |
| : next_(nullptr), |
| image_(FillLayer::InitialFillImage(type)), |
| position_x_(FillLayer::InitialFillPositionX(type)), |
| position_y_(FillLayer::InitialFillPositionY(type)), |
| size_length_(FillLayer::InitialFillSizeLength(type)), |
| attachment_( |
| static_cast<unsigned>(FillLayer::InitialFillAttachment(type))), |
| clip_(static_cast<unsigned>(FillLayer::InitialFillClip(type))), |
| origin_(static_cast<unsigned>(FillLayer::InitialFillOrigin(type))), |
| repeat_x_(static_cast<unsigned>(FillLayer::InitialFillRepeatX(type))), |
| repeat_y_(static_cast<unsigned>(FillLayer::InitialFillRepeatY(type))), |
| composite_(FillLayer::InitialFillComposite(type)), |
| size_type_( |
| use_initial_values |
| ? static_cast<unsigned>(FillLayer::InitialFillSizeType(type)) |
| : static_cast<unsigned>(EFillSizeType::kSizeNone)), |
| blend_mode_(static_cast<unsigned>(FillLayer::InitialFillBlendMode(type))), |
| mask_source_type_( |
| static_cast<unsigned>(FillLayer::InitialFillMaskSourceType(type))), |
| background_x_origin_(static_cast<unsigned>(BackgroundEdgeOrigin::kLeft)), |
| background_y_origin_(static_cast<unsigned>(BackgroundEdgeOrigin::kTop)), |
| image_set_(use_initial_values), |
| attachment_set_(use_initial_values), |
| clip_set_(use_initial_values), |
| origin_set_(use_initial_values), |
| repeat_x_set_(use_initial_values), |
| repeat_y_set_(use_initial_values), |
| pos_x_set_(use_initial_values), |
| pos_y_set_(use_initial_values), |
| background_x_origin_set_(false), |
| background_y_origin_set_(false), |
| composite_set_(use_initial_values || type == EFillLayerType::kMask), |
| blend_mode_set_(use_initial_values), |
| mask_source_type_set_(use_initial_values), |
| type_(static_cast<unsigned>(type)), |
| layers_clip_max_(0), |
| any_layer_uses_content_box_(false), |
| any_layer_has_image_(false), |
| any_layer_has_local_attachment_image_(false), |
| any_layer_has_fixed_attachment_image_(false), |
| any_layer_has_default_attachment_image_(false), |
| cached_properties_computed_(false) {} |
| |
| FillLayer::FillLayer(const FillLayer& o) |
| : next_(o.next_ ? new FillLayer(*o.next_) : nullptr), |
| image_(o.image_), |
| position_x_(o.position_x_), |
| position_y_(o.position_y_), |
| size_length_(o.size_length_), |
| attachment_(o.attachment_), |
| clip_(o.clip_), |
| origin_(o.origin_), |
| repeat_x_(o.repeat_x_), |
| repeat_y_(o.repeat_y_), |
| composite_(o.composite_), |
| size_type_(o.size_type_), |
| blend_mode_(o.blend_mode_), |
| mask_source_type_(o.mask_source_type_), |
| background_x_origin_(o.background_x_origin_), |
| background_y_origin_(o.background_y_origin_), |
| image_set_(o.image_set_), |
| attachment_set_(o.attachment_set_), |
| clip_set_(o.clip_set_), |
| origin_set_(o.origin_set_), |
| repeat_x_set_(o.repeat_x_set_), |
| repeat_y_set_(o.repeat_y_set_), |
| pos_x_set_(o.pos_x_set_), |
| pos_y_set_(o.pos_y_set_), |
| background_x_origin_set_(o.background_x_origin_set_), |
| background_y_origin_set_(o.background_y_origin_set_), |
| composite_set_(o.composite_set_), |
| blend_mode_set_(o.blend_mode_set_), |
| mask_source_type_set_(o.mask_source_type_set_), |
| type_(o.type_), |
| layers_clip_max_(0), |
| any_layer_uses_content_box_(false), |
| any_layer_has_image_(false), |
| any_layer_has_local_attachment_image_(false), |
| any_layer_has_fixed_attachment_image_(false), |
| any_layer_has_default_attachment_image_(false), |
| cached_properties_computed_(false) {} |
| |
| FillLayer::~FillLayer() { |
| delete next_; |
| } |
| |
| FillLayer& FillLayer::operator=(const FillLayer& o) { |
| if (next_ != o.next_) { |
| delete next_; |
| next_ = o.next_ ? new FillLayer(*o.next_) : nullptr; |
| } |
| |
| image_ = o.image_; |
| position_x_ = o.position_x_; |
| position_y_ = o.position_y_; |
| background_x_origin_ = o.background_x_origin_; |
| background_y_origin_ = o.background_y_origin_; |
| background_x_origin_set_ = o.background_x_origin_set_; |
| background_y_origin_set_ = o.background_y_origin_set_; |
| size_length_ = o.size_length_; |
| attachment_ = o.attachment_; |
| clip_ = o.clip_; |
| composite_ = o.composite_; |
| blend_mode_ = o.blend_mode_; |
| origin_ = o.origin_; |
| repeat_x_ = o.repeat_x_; |
| repeat_y_ = o.repeat_y_; |
| size_type_ = o.size_type_; |
| mask_source_type_ = o.mask_source_type_; |
| |
| image_set_ = o.image_set_; |
| attachment_set_ = o.attachment_set_; |
| clip_set_ = o.clip_set_; |
| composite_set_ = o.composite_set_; |
| blend_mode_set_ = o.blend_mode_set_; |
| origin_set_ = o.origin_set_; |
| repeat_x_set_ = o.repeat_x_set_; |
| repeat_y_set_ = o.repeat_y_set_; |
| pos_x_set_ = o.pos_x_set_; |
| pos_y_set_ = o.pos_y_set_; |
| mask_source_type_set_ = o.mask_source_type_set_; |
| |
| type_ = o.type_; |
| |
| cached_properties_computed_ = false; |
| |
| return *this; |
| } |
| |
| bool FillLayer::LayerPropertiesEqual(const FillLayer& o) const { |
| return DataEquivalent(image_, o.image_) && position_x_ == o.position_x_ && |
| position_y_ == o.position_y_ && |
| background_x_origin_ == o.background_x_origin_ && |
| background_y_origin_ == o.background_y_origin_ && |
| attachment_ == o.attachment_ && clip_ == o.clip_ && |
| composite_ == o.composite_ && blend_mode_ == o.blend_mode_ && |
| origin_ == o.origin_ && repeat_x_ == o.repeat_x_ && |
| repeat_y_ == o.repeat_y_ && size_type_ == o.size_type_ && |
| mask_source_type_ == o.mask_source_type_ && |
| size_length_ == o.size_length_ && type_ == o.type_; |
| } |
| |
| bool FillLayer::operator==(const FillLayer& o) const { |
| return LayerPropertiesEqual(o) && |
| ((next_ && o.next_) ? *next_ == *o.next_ : next_ == o.next_); |
| } |
| |
| bool FillLayer::VisuallyEqual(const FillLayer& o) const { |
| if (image_ || o.image_) { |
| if (!LayerPropertiesEqual(o)) |
| return false; |
| } else if (clip_ != o.clip_) { |
| return false; |
| } |
| if (next_ && o.next_) |
| return next_->VisuallyEqual(*o.next_); |
| return next_ == o.next_; |
| } |
| |
| void FillLayer::FillUnsetProperties() { |
| FillLayer* curr; |
| for (curr = this; curr && curr->IsPositionXSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->position_x_ = pattern->position_x_; |
| if (pattern->IsBackgroundXOriginSet()) |
| curr->background_x_origin_ = pattern->background_x_origin_; |
| if (pattern->IsBackgroundYOriginSet()) |
| curr->background_y_origin_ = pattern->background_y_origin_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsPositionYSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->position_y_ = pattern->position_y_; |
| if (pattern->IsBackgroundXOriginSet()) |
| curr->background_x_origin_ = pattern->background_x_origin_; |
| if (pattern->IsBackgroundYOriginSet()) |
| curr->background_y_origin_ = pattern->background_y_origin_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsAttachmentSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->attachment_ = pattern->attachment_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsClipSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->clip_ = pattern->clip_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsCompositeSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->composite_ = pattern->composite_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsBlendModeSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->blend_mode_ = pattern->blend_mode_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsOriginSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->origin_ = pattern->origin_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsRepeatXSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->repeat_x_ = pattern->repeat_x_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsRepeatYSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->repeat_y_ = pattern->repeat_y_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| |
| for (curr = this; curr && curr->IsSizeSet(); curr = curr->Next()) { |
| } |
| if (curr && curr != this) { |
| // We need to fill in the remaining values with the pattern specified. |
| for (FillLayer* pattern = this; curr; curr = curr->Next()) { |
| curr->size_type_ = pattern->size_type_; |
| curr->size_length_ = pattern->size_length_; |
| pattern = pattern->Next(); |
| if (pattern == curr || !pattern) |
| pattern = this; |
| } |
| } |
| } |
| |
| void FillLayer::CullEmptyLayers() { |
| FillLayer* next; |
| for (FillLayer* p = this; p; p = next) { |
| next = p->next_; |
| if (next && !next->IsImageSet()) { |
| delete next; |
| p->next_ = nullptr; |
| break; |
| } |
| } |
| } |
| |
| void FillLayer::ComputeCachedProperties() const { |
| DCHECK(!cached_properties_computed_); |
| |
| layers_clip_max_ = static_cast<unsigned>(Clip()); |
| any_layer_uses_content_box_ = |
| Clip() == EFillBox::kContent || Origin() == EFillBox::kContent; |
| any_layer_has_image_ = !!GetImage(); |
| any_layer_has_local_attachment_image_ = |
| any_layer_has_image_ && Attachment() == EFillAttachment::kLocal; |
| any_layer_has_fixed_attachment_image_ = |
| any_layer_has_image_ && Attachment() == EFillAttachment::kFixed; |
| any_layer_has_default_attachment_image_ = |
| any_layer_has_image_ && Attachment() == EFillAttachment::kScroll; |
| cached_properties_computed_ = true; |
| |
| if (next_) { |
| next_->ComputeCachedPropertiesIfNeeded(); |
| layers_clip_max_ = static_cast<unsigned>( |
| EnclosingFillBox(LayersClipMax(), next_->LayersClipMax())); |
| any_layer_uses_content_box_ |= next_->any_layer_uses_content_box_; |
| any_layer_has_image_ |= next_->any_layer_has_image_; |
| any_layer_has_local_attachment_image_ |= |
| next_->any_layer_has_local_attachment_image_; |
| any_layer_has_fixed_attachment_image_ |= |
| next_->any_layer_has_fixed_attachment_image_; |
| any_layer_has_default_attachment_image_ |= |
| next_->any_layer_has_default_attachment_image_; |
| } |
| } |
| |
| bool FillLayer::ClipOccludesNextLayers() const { |
| return Clip() == LayersClipMax(); |
| } |
| |
| bool FillLayer::ImagesAreLoaded() const { |
| const FillLayer* curr; |
| for (curr = this; curr; curr = curr->Next()) { |
| if (curr->image_ && !curr->image_->IsLoaded()) |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool FillLayer::ImageIsOpaque(const Document& document, |
| const ComputedStyle& style) const { |
| // Returns whether we have an image that will cover the content below it when |
| // composite_ == CompositeSourceOver && blend_mode_ == BlendMode::kNormal. |
| return image_->KnownToBeOpaque(document, style) && |
| !image_->ImageSize(document, style.EffectiveZoom(), LayoutSize()) |
| .IsEmpty(); |
| } |
| |
| bool FillLayer::ImageTilesLayer() const { |
| // Returns true if an image will be tiled such that it covers any sized |
| // rectangle. |
| // TODO(schenney) We could relax the repeat mode requirement if we also knew |
| // the rect we had to fill, and the portion of the image we need to use, and |
| // know that the latter covers the former. |
| return (static_cast<EFillRepeat>(repeat_x_) == EFillRepeat::kRepeatFill || |
| static_cast<EFillRepeat>(repeat_x_) == EFillRepeat::kRoundFill) && |
| (static_cast<EFillRepeat>(repeat_y_) == EFillRepeat::kRepeatFill || |
| static_cast<EFillRepeat>(repeat_y_) == EFillRepeat::kRoundFill); |
| } |
| |
| bool FillLayer::ImageOccludesNextLayers(const Document& document, |
| const ComputedStyle& style) const { |
| // We can't cover without an image, regardless of other parameters |
| if (!image_ || !image_->CanRender()) |
| return false; |
| |
| switch (composite_) { |
| case kCompositeClear: |
| case kCompositeCopy: |
| return ImageTilesLayer(); |
| case kCompositeSourceOver: |
| return (blend_mode_ == static_cast<unsigned>(BlendMode::kNormal)) && |
| ImageTilesLayer() && ImageIsOpaque(document, style); |
| default: {} |
| } |
| |
| return false; |
| } |
| |
| static inline bool LayerImagesIdentical(const FillLayer& layer1, |
| const FillLayer& layer2) { |
| // We just care about pointer equivalency. |
| return layer1.GetImage() == layer2.GetImage(); |
| } |
| |
| bool FillLayer::ImagesIdentical(const FillLayer* layer1, |
| const FillLayer* layer2) { |
| for (; layer1 && layer2; layer1 = layer1->Next(), layer2 = layer2->Next()) { |
| if (!LayerImagesIdentical(*layer1, *layer2)) |
| return false; |
| } |
| |
| return !layer1 && !layer2; |
| } |
| |
| } // namespace blink |