Beispiel #1
0
unsigned
DrawingGroup::_updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
{
    unsigned beststate = STATE_ALL;
    bool outline = _drawing.outline();

    UpdateContext child_ctx(ctx);
    if (_child_transform) {
        child_ctx.ctm = *_child_transform * ctx.ctm;
    }
    for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
        i->update(area, child_ctx, flags, reset);
    }
    if (beststate & STATE_BBOX) {
        _bbox = Geom::OptIntRect();
        for (ChildrenList::iterator i = _children.begin(); i != _children.end(); ++i) {
            if (i->visible()) {
                _bbox.unionWith(outline ? i->geometricBounds() : i->visualBounds());
            }
        }
    }
    return beststate;
}
Beispiel #2
0
void Object::set_property_value_impl(ContextType& ctx, const Property &property,
                                     ValueType value, bool try_update, bool is_default)
{
    ctx.will_change(*this, property);

    auto& table = *m_row.get_table();
    size_t col = property.table_column;
    size_t row = m_row.get_index();
    if (is_nullable(property.type) && ctx.is_null(value)) {
        if (property.type == PropertyType::Object) {
            if (!is_default)
                table.nullify_link(col, row);
        }
        else {
            table.set_null(col, row, is_default);
        }

        ctx.did_change();
        return;
    }

    if (is_array(property.type)) {
        if (property.type == PropertyType::LinkingObjects)
            throw ReadOnlyPropertyException(m_object_schema->name, property.name);
        REALM_ASSERT(property.type == PropertyType::Object);

        List list(m_realm, m_row.get_linklist(col));
        list.remove_all();
        if (!ctx.is_null(value)) {
            ContextType child_ctx(ctx, property);
            ctx.enumerate_list(value, [&](auto&& element) {
                list.add(child_ctx, element, try_update);
            });
        }
        ctx.did_change();
        return;
    }

    switch (property.type & ~PropertyType::Flags) {
        case PropertyType::Bool:
            table.set(col, row, ctx.template unbox<bool>(value), is_default);
            break;
        case PropertyType::Int:
            table.set(col, row, ctx.template unbox<int64_t>(value), is_default);
            break;
        case PropertyType::Float:
            table.set(col, row, ctx.template unbox<float>(value), is_default);
            break;
        case PropertyType::Double:
            table.set(col, row, ctx.template unbox<double>(value), is_default);
            break;
        case PropertyType::String:
            table.set(col, row, ctx.template unbox<StringData>(value), is_default);
            break;
        case PropertyType::Data:
            table.set(col, row, ctx.template unbox<BinaryData>(value), is_default);
            break;
        case PropertyType::Any:
            throw std::logic_error("not supported");
        case PropertyType::Date:
            table.set(col, row, ctx.template unbox<Timestamp>(value), is_default);
            break;
        case PropertyType::Object: {
            ContextType child_ctx(ctx, property);
            auto link = child_ctx.template unbox<RowExpr>(value, true, try_update);
            table.set_link(col, row, link.get_index(), is_default);
            break;
        }
        default:
            REALM_COMPILER_HINT_UNREACHABLE();
    }
    ctx.did_change();
}
void Object::set_property_value_impl(ContextType& ctx, const Property &property,
                                     ValueType value, bool try_update, bool update_only_diff, bool is_default)
{
    ctx.will_change(*this, property);

    auto& table = *m_row.get_table();
    size_t col = property.table_column;
    size_t row = m_row.get_index();
    if (is_nullable(property.type) && ctx.is_null(value)) {
        if (!update_only_diff || !table.is_null(col, row)) {
            if (property.type == PropertyType::Object) {
                if (!is_default)
                    table.nullify_link(col, row);
            }
            else {
                table.set_null(col, row, is_default);
            }
        }

        ctx.did_change();
        return;
    }

    if (is_array(property.type)) {
        if (property.type == PropertyType::LinkingObjects)
            throw ReadOnlyPropertyException(m_object_schema->name, property.name);

        ContextType child_ctx(ctx, property);
        List list(m_realm, table, col, m_row.get_index());
        list.assign(child_ctx, value, try_update, update_only_diff);
        ctx.did_change();
        return;
    }

    switch (property.type & ~PropertyType::Nullable) {
        case PropertyType::Object: {
            ContextType child_ctx(ctx, property);
            auto curr_link = table.get_link(col,row);
            auto link = child_ctx.template unbox<RowExpr>(value, true, try_update, update_only_diff, curr_link);
            if (!update_only_diff || curr_link != link.get_index()) {
                table.set_link(col, row, link.get_index(), is_default);
            }
            break;
        }
        case PropertyType::Bool:
            do_update_value<bool>(ctx, table, value, col, row, update_only_diff, is_default);
            break;
        case PropertyType::Int:
            do_update_value<int64_t>(ctx, table, value, col, row, update_only_diff, is_default);
            break;
        case PropertyType::Float:
            do_update_value<float>(ctx, table, value, col, row, update_only_diff, is_default);
            break;
        case PropertyType::Double:
            do_update_value<double>(ctx, table, value, col, row, update_only_diff, is_default);
            break;
        case PropertyType::String:
            do_update_value<StringData>(ctx, table, value, col, row, update_only_diff, is_default);
            break;
        case PropertyType::Data:
            do_update_value<BinaryData>(ctx, table, value, col, row, update_only_diff, is_default);
            break;
        case PropertyType::Date:
            do_update_value<Timestamp>(ctx, table, value, col, row, update_only_diff, is_default);
            break;
        case PropertyType::Any:
            throw std::logic_error("not supported");
        default:
            REALM_COMPILER_HINT_UNREACHABLE();
    }
    ctx.did_change();
}
Beispiel #4
0
/**
 * Update derived data before operations.
 * The purpose of this call is to recompute internal data which depends
 * on the attributes of the object, but is not directly settable by the user.
 * Precomputing this data speeds up later rendering, because some items
 * can be omitted.
 *
 * Currently this method handles updating the visual and geometric bounding boxes
 * in pixels, storing the total transformation from item space to the screen
 * and cache invalidation.
 *
 * @param area Area to which the update should be restricted. Only takes effect
 *             if the bounding box is known.
 * @param ctx A structure to store cascading state.
 * @param flags Which internal data should be recomputed. This can be any combination
 *              of StateFlags.
 * @param reset State fields that should be reset before processing them. This is
 *              a means to force a recomputation of internal data even if the item
 *              considers it up to date. Mainly for internal use, such as
 *              propagating bounding box recomputation to children when the item's
 *              transform changes.
 */
void
DrawingItem::update(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset)
{
    bool render_filters = _drawing.renderFilters();
    bool outline = _drawing.outline();

    // Set reset flags according to propagation status
    reset |= _propagate_state;
    _propagate_state = 0;

    _state &= ~reset; // reset state of this item

    if ((~_state & flags) == 0) return;  // nothing to do

    // TODO this might be wrong
    if (_state & STATE_BBOX) {
        // we have up-to-date bbox
        if (!area.intersects(outline ? _bbox : _drawbox)) return;
    }

    // compute which elements need an update
    unsigned to_update = _state ^ flags;

    // this needs to be called before we recurse into children
    if (to_update & STATE_BACKGROUND) {
        _background_accumulate = _background_new;
        if (_child_type == CHILD_NORMAL && _parent->_background_accumulate)
            _background_accumulate = true;
    }

    UpdateContext child_ctx(ctx);
    if (_transform) {
        child_ctx.ctm = *_transform * ctx.ctm;
    }
    /* Remember the transformation matrix */
    Geom::Affine ctm_change = _ctm.inverse() * child_ctx.ctm;
    _ctm = child_ctx.ctm;

    // update _bbox and call this function for children
    _state = _updateItem(area, child_ctx, flags, reset);

    if (to_update & STATE_BBOX) {
        // compute drawbox
        if (_filter && render_filters) {
            Geom::OptRect enlarged = _filter->filter_effect_area(_item_bbox);
            if (enlarged) {
                *enlarged *= ctm();
                _drawbox = enlarged->roundOutwards();
            } else {
                _drawbox = Geom::OptIntRect();
            }
        } else {
            _drawbox = _bbox;
        }

        // Clipping
        if (_clip) {
            _clip->update(area, child_ctx, flags, reset);
            if (outline) {
                _bbox.unionWith(_clip->_bbox);
            } else {
                _drawbox.intersectWith(_clip->_bbox);
            }
        }
        // Masking
        if (_mask) {
            _mask->update(area, child_ctx, flags, reset);
            if (outline) {
                _bbox.unionWith(_mask->_bbox);
            } else {
                // for masking, we need full drawbox of mask
                _drawbox.intersectWith(_mask->_drawbox);
            }
        }
    }

    if (to_update & STATE_CACHE) {
        // Update cache score for this item
        if (_has_cache_iterator) {
            // remove old score information
            _drawing._candidate_items.erase(_cache_iterator);
            _has_cache_iterator = false;
        }
        double score = _cacheScore();
        if (score >= _drawing._cache_score_threshold) {
            CacheRecord cr;
            cr.score = score;
            // if _cacheRect() is empty, a negative score will be returned from _cacheScore(),
            // so this will not execute (cache score threshold must be positive)
            cr.cache_size = _cacheRect()->area() * 4;
            cr.item = this;
            _drawing._candidate_items.push_front(cr);
            _cache_iterator = _drawing._candidate_items.begin();
            _has_cache_iterator = true;
        }

        /* Update cache if enabled.
         * General note: here we only tell the cache how it has to transform
         * during the render phase. The transformation is deferred because
         * after the update the item can have its caching turned off,
         * e.g. because its filter was removed. This way we avoid tempoerarily
         * using more memory than the cache budget */
        if (_cache) {
            Geom::OptIntRect cl = _cacheRect();
            if (_visible && cl) { // never create cache for invisible items
                // this takes care of invalidation on transform
                _cache->scheduleTransform(*cl, ctm_change);
            } else {
                // Destroy cache for this item - outside of canvas or invisible.
                // The opposite transition (invisible -> visible or object
                // entering the canvas) is handled during the render phase
                delete _cache;
                _cache = NULL;
            }
        }
    }

    if (to_update & STATE_RENDER) {
        // now that we know drawbox, dirty the corresponding rect on canvas
        // unless filtered, groups do not need to render by themselves, only their members
        if (_fill_pattern) {
            _fill_pattern->update(area, child_ctx, flags, reset);
        }
        if (_stroke_pattern) {
            _stroke_pattern->update(area, child_ctx, flags, reset);
        }
        if (!is_drawing_group(this) || (_filter && render_filters)) {
            _markForRendering();
        }
    }
}