/* * Copyright (C) 2010 The Android Open Source Project * * 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. */ #pragma once #include "Vertex.h" #include <utils/Log.h> #include <algorithm> #include <cmath> #include <iomanip> #include <ostream> #include <SkRect.h> namespace android { namespace uirenderer { #define RECT_STRING "%5.2f %5.2f %5.2f %5.2f" #define RECT_ARGS(r) \ (r).left, (r).top, (r).right, (r).bottom #define SK_RECT_ARGS(r) \ (r).left(), (r).top(), (r).right(), (r).bottom() /////////////////////////////////////////////////////////////////////////////// // Structs /////////////////////////////////////////////////////////////////////////////// class Rect { public: float left; float top; float right; float bottom; // Used by Region typedef float value_type; // we don't provide copy-ctor and operator= on purpose // because we want the compiler generated versions inline Rect(): left(0), top(0), right(0), bottom(0) { } inline Rect(float left, float top, float right, float bottom): left(left), top(top), right(right), bottom(bottom) { } inline Rect(float width, float height): left(0.0f), top(0.0f), right(width), bottom(height) { } inline Rect(const SkRect& rect): left(rect.fLeft), top(rect.fTop), right(rect.fRight), bottom(rect.fBottom) { } friend int operator==(const Rect& a, const Rect& b) { return !memcmp(&a, &b, sizeof(a)); } friend int operator!=(const Rect& a, const Rect& b) { return memcmp(&a, &b, sizeof(a)); } inline void clear() { left = top = right = bottom = 0.0f; } inline bool isEmpty() const { // this is written in such way this it'll handle NANs to return // true (empty) return !((left < right) && (top < bottom)); } inline void setEmpty() { left = top = right = bottom = 0.0f; } inline void set(float left, float top, float right, float bottom) { this->left = left; this->right = right; this->top = top; this->bottom = bottom; } inline void set(const Rect& r) { set(r.left, r.top, r.right, r.bottom); } inline void set(const SkIRect& r) { set(r.left(), r.top(), r.right(), r.bottom()); } inline float getWidth() const { return right - left; } inline float getHeight() const { return bottom - top; } bool intersects(float l, float t, float r, float b) const { float tempLeft = std::max(left, l); float tempTop = std::max(top, t); float tempRight = std::min(right, r); float tempBottom = std::min(bottom, b); return ((tempLeft < tempRight) && (tempTop < tempBottom)); // !isEmpty } bool intersects(const Rect& r) const { return intersects(r.left, r.top, r.right, r.bottom); } /** * This method is named 'doIntersect' instead of 'intersect' so as not to be confused with * SkRect::intersect / android.graphics.Rect#intersect behavior, which do not modify the object * if the intersection of the rects would be empty. */ void doIntersect(float l, float t, float r, float b) { left = std::max(left, l); top = std::max(top, t); right = std::min(right, r); bottom = std::min(bottom, b); } void doIntersect(const Rect& r) { doIntersect(r.left, r.top, r.right, r.bottom); } inline bool contains(float l, float t, float r, float b) const { return l >= left && t >= top && r <= right && b <= bottom; } inline bool contains(const Rect& r) const { return contains(r.left, r.top, r.right, r.bottom); } bool unionWith(const Rect& r) { if (r.left < r.right && r.top < r.bottom) { if (left < right && top < bottom) { if (left > r.left) left = r.left; if (top > r.top) top = r.top; if (right < r.right) right = r.right; if (bottom < r.bottom) bottom = r.bottom; return true; } else { left = r.left; top = r.top; right = r.right; bottom = r.bottom; return true; } } return false; } void translate(float dx, float dy) { left += dx; right += dx; top += dy; bottom += dy; } void inset(float delta) { outset(-delta); } void outset(float delta) { left -= delta; top -= delta; right += delta; bottom += delta; } void outset(float xdelta, float ydelta) { left -= xdelta; top -= ydelta; right += xdelta; bottom += ydelta; } /** * Similar to snapToPixelBoundaries, but estimates bounds conservatively to handle GL rounding * errors. * * This function should be used whenever estimating the damage rect of geometry already mapped * into layer space. */ void snapGeometryToPixelBoundaries(bool snapOut) { if (snapOut) { /* For AA geometry with a ramp perimeter, don't snap by rounding - AA geometry will have * a 0.5 pixel perimeter not accounted for in its bounds. Instead, snap by * conservatively rounding out the bounds with floor/ceil. * * In order to avoid changing integer bounds with floor/ceil due to rounding errors * inset the bounds first by the fudge factor. Very small fraction-of-a-pixel errors * from this inset will only incur similarly small errors in output, due to transparency * in extreme outside of the geometry. */ left = floorf(left + Vertex::GeometryFudgeFactor()); top = floorf(top + Vertex::GeometryFudgeFactor()); right = ceilf(right - Vertex::GeometryFudgeFactor()); bottom = ceilf(bottom - Vertex::GeometryFudgeFactor()); } else { /* For other geometry, we do the regular rounding in order to snap, but also outset the * bounds by a fudge factor. This ensures that ambiguous geometry (e.g. a non-AA Rect * with top left at (0.5, 0.5)) will err on the side of a larger damage rect. */ left = floorf(left + 0.5f - Vertex::GeometryFudgeFactor()); top = floorf(top + 0.5f - Vertex::GeometryFudgeFactor()); right = floorf(right + 0.5f + Vertex::GeometryFudgeFactor()); bottom = floorf(bottom + 0.5f + Vertex::GeometryFudgeFactor()); } } void snapToPixelBoundaries() { left = floorf(left + 0.5f); top = floorf(top + 0.5f); right = floorf(right + 0.5f); bottom = floorf(bottom + 0.5f); } void roundOut() { left = floorf(left); top = floorf(top); right = ceilf(right); bottom = ceilf(bottom); } /* * Similar to unionWith, except this makes the assumption that both rects are non-empty * to avoid both emptiness checks. */ void expandToCover(const Rect& other) { left = std::min(left, other.left); top = std::min(top, other.top); right = std::max(right, other.right); bottom = std::max(bottom, other.bottom); } void expandToCover(float x, float y) { left = std::min(left, x); top = std::min(top, y); right = std::max(right, x); bottom = std::max(bottom, y); } SkRect toSkRect() const { return SkRect::MakeLTRB(left, top, right, bottom); } SkIRect toSkIRect() const { return SkIRect::MakeLTRB(left, top, right, bottom); } void dump(const char* label = nullptr) const { ALOGD("%s[l=%.2f t=%.2f r=%.2f b=%.2f]", label ? label : "Rect", left, top, right, bottom); } friend std::ostream& operator<<(std::ostream& os, const Rect& rect) { if (rect.isEmpty()) { // Print empty, but continue, since empty rects may still have useful coordinate info os << "(empty)"; } if (rect.left == 0 && rect.top == 0) { return os << "[" << rect.right << " x " << rect.bottom << "]"; } return os << "[" << rect.left << " " << rect.top << " " << rect.right << " " << rect.bottom << "]"; } }; // class Rect }; // namespace uirenderer }; // namespace android