hwcutils.cpp \
platform.cpp \
platformdrmgeneric.cpp \
- separate_rects.cpp \
vsyncworker.cpp
LOCAL_CFLAGS := $(common_drm_hwcomposer_cflags)
return 0;
}
-void DrmDisplayComposition::SeparateLayers(DrmHwcRect<int> *, size_t) {
- std::vector<size_t> dedicated_layers;
-
- // Go through the composition and find the layers that have a dedicated plane.
- for (auto &i : composition_planes_) {
- if (i.type() == DrmCompositionPlane::Type::kLayer) {
- dedicated_layers.insert(dedicated_layers.end(), i.source_layers().begin(),
- i.source_layers().end());
- }
- }
-}
-
int DrmDisplayComposition::Plan(std::vector<DrmPlane *> *primary_planes,
std::vector<DrmPlane *> *overlay_planes) {
if (type_ != DRM_COMPOSITION_TYPE_FRAME)
*out << " transform=";
DumpTransform(layer.transform, out);
*out << " blending[a=" << (int)layer.alpha
- << "]=" << BlendingToString(layer.blending) << " source_crop";
- layer.source_crop.Dump(out);
- *out << " display_frame";
- layer.display_frame.Dump(out);
-
- *out << "\n";
+ << "]=" << BlendingToString(layer.blending) << "\n";
}
*out << " Planes: count=" << composition_planes_.size() << "\n";
};
struct DrmCompositionRegion {
- DrmHwcRect<int> frame;
std::vector<size_t> source_layers;
};
int Plan(std::vector<DrmPlane *> *primary_planes,
std::vector<DrmPlane *> *overlay_planes);
- int FinalizeComposition();
-
std::vector<DrmHwcLayer> &layers() {
return layers_;
}
private:
bool validate_composition_type(DrmCompositionType desired);
- int FinalizeComposition(DrmHwcRect<int> *exclude_rects,
- size_t num_exclude_rects);
- void SeparateLayers(DrmHwcRect<int> *exclude_rects, size_t num_exclude_rects);
-
DrmResources *drm_ = NULL;
DrmCrtc *crtc_ = NULL;
Importer *importer_ = NULL;
int fb_id = -1;
int fence_fd = -1;
- DrmHwcRect<int> display_frame;
- DrmHwcRect<float> source_crop;
+ hwc_rect_t display_frame;
+ hwc_frect_t source_crop;
uint64_t rotation = 0;
uint64_t alpha = 0xFFFF;
#include "drmhwcomposer.h"
#include "drmdisplaycomposition.h"
#include "drmframebuffer.h"
-#include "separate_rects.h"
#include <pthread.h>
#include <memory>
#include <stdbool.h>
#include <stdint.h>
+#include <vector>
+
#include <hardware/hardware.h>
#include <hardware/hwcomposer.h>
#include "autofd.h"
-#include "separate_rects.h"
#include "drmhwcgralloc.h"
struct hwc_import_context;
native_handle_t *handle_ = NULL;
};
-template <typename T>
-using DrmHwcRect = separate_rects::Rect<T>;
-
enum DrmHwcTransform {
kIdentity = 0,
kFlipH = 1 << 0,
uint32_t transform;
DrmHwcBlending blending = DrmHwcBlending::kNone;
uint16_t alpha = 0xffff;
- DrmHwcRect<float> source_crop;
- DrmHwcRect<int> display_frame;
+ hwc_frect_t source_crop;
+ hwc_rect_t display_frame;
UniqueFd acquire_fence;
OutputFd release_fence;
}
void DrmHwcLayer::SetSourceCrop(hwc_frect_t const &crop) {
- source_crop = DrmHwcRect<float>(crop.left, crop.top, crop.right, crop.bottom);
+ source_crop = crop;
}
void DrmHwcLayer::SetDisplayFrame(hwc_rect_t const &frame) {
- display_frame =
- DrmHwcRect<int>(frame.left, frame.top, frame.right, frame.bottom);
+ display_frame = frame;
}
void DrmHwcLayer::SetTransform(int32_t sf_transform) {
+++ /dev/null
-/*
- * Copyright (C) 2015 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.
- */
-
-#include "separate_rects.h"
-#include <algorithm>
-#include <assert.h>
-#include <iostream>
-#include <map>
-#include <set>
-#include <utility>
-#include <vector>
-
-namespace separate_rects {
-
-enum EventType { START, END };
-
-template <typename TId, typename TNum>
-struct StartedRect {
- IdSet<TId> id_set;
- TNum left, top, bottom;
-
- // Note that this->left is not part of the key. That field is only to mark the
- // left edge of the rectangle.
- bool operator<(const StartedRect<TId, TNum> &rhs) const {
- return (top < rhs.top || (top == rhs.top && bottom < rhs.bottom)) ||
- (top == rhs.top && bottom == rhs.bottom && id_set < rhs.id_set);
- }
-};
-
-template <typename TId, typename TNum>
-struct SweepEvent {
- EventType type;
- union {
- TNum x;
- TNum y;
- };
-
- TId rect_id;
-
- bool operator<(const SweepEvent<TId, TNum> &rhs) const {
- return (y < rhs.y || (y == rhs.y && rect_id < rhs.rect_id));
- }
-};
-
-template <typename TNum>
-std::ostream &operator<<(std::ostream &os, const Rect<TNum> &rect) {
- return os << rect.bounds[0] << ", " << rect.bounds[1] << ", "
- << rect.bounds[2] << ", " << rect.bounds[3];
-}
-
-template <typename TUInt>
-std::ostream &operator<<(std::ostream &os, const IdSet<TUInt> &obj) {
- int bits = IdSet<TUInt>::max_elements;
- TUInt mask = ((TUInt)0x1) << (bits - 1);
- for (int i = 0; i < bits; i++)
- os << ((obj.getBits() & (mask >> i)) ? "1" : "0");
- return os;
-}
-
-template <typename TNum, typename TId>
-void separate_rects(const std::vector<Rect<TNum>> &in,
- std::vector<RectSet<TId, TNum>> *out) {
- // Overview:
- // This algorithm is a line sweep algorithm that travels from left to right.
- // The sweep stops at each vertical edge of each input rectangle in sorted
- // order of x-coordinate. At each stop, the sweep line is examined in order of
- // y-coordinate from top to bottom. Along the way, a running set of rectangle
- // IDs is either added to or subtracted from as the top and bottom edges are
- // encountered, respectively. At each change of that running set, a copy of
- // that set is recorded in along with the the y-coordinate it happened at in a
- // list. This list is then interpreted as a sort of vertical cross section of
- // our output set of non-overlapping rectangles. Based of the algorithm found
- // at: http://stackoverflow.com/a/2755498
-
- if (in.size() > IdSet<TId>::max_elements) {
- return;
- }
-
- // Events are when the sweep line encounters the starting or ending edge of
- // any input rectangle.
- std::set<SweepEvent<TId, TNum>> sweep_h_events; // Left or right bounds
- std::set<SweepEvent<TId, TNum>> sweep_v_events; // Top or bottom bounds
-
- // A started rect is a rectangle whose left, top, bottom edge, and set of
- // rectangle IDs is known. The key of this map includes all that information
- // (except the left edge is never used to determine key equivalence or
- // ordering),
- std::map<StartedRect<TId, TNum>, bool> started_rects;
-
- // This is cleared after every event. Its declaration is here to avoid
- // reallocating a vector and its buffers every event.
- std::vector<std::pair<TNum, IdSet<TId>>> active_regions;
-
- // This pass will add rectangle start and end events to be triggered as the
- // algorithm sweeps from left to right.
- for (TId i = 0; i < in.size(); i++) {
- const Rect<TNum> &rect = in[i];
-
- // Filter out empty or invalid rects.
- if (rect.left >= rect.right || rect.top >= rect.bottom)
- continue;
-
- SweepEvent<TId, TNum> evt;
- evt.rect_id = i;
-
- evt.type = START;
- evt.x = rect.left;
- sweep_h_events.insert(evt);
-
- evt.type = END;
- evt.x = rect.right;
- sweep_h_events.insert(evt);
- }
-
- for (typename std::set<SweepEvent<TId, TNum>>::iterator it =
- sweep_h_events.begin();
- it != sweep_h_events.end(); ++it) {
- const SweepEvent<TId, TNum> &h_evt = *it;
- const Rect<TNum> &rect = in[h_evt.rect_id];
-
- // During this event, we have encountered a vertical starting or ending edge
- // of a rectangle so want to append or remove (respectively) that rectangles
- // top and bottom from the vertical sweep line.
- SweepEvent<TId, TNum> v_evt;
- v_evt.rect_id = h_evt.rect_id;
- if (h_evt.type == START) {
- v_evt.type = START;
- v_evt.y = rect.top;
- sweep_v_events.insert(v_evt);
-
- v_evt.type = END;
- v_evt.y = rect.bottom;
- sweep_v_events.insert(v_evt);
- } else {
- v_evt.type = START;
- v_evt.y = rect.top;
- typename std::set<SweepEvent<TId, TNum>>::iterator start_it =
- sweep_v_events.find(v_evt);
- assert(start_it != sweep_v_events.end());
- sweep_v_events.erase(start_it);
-
- v_evt.type = END;
- v_evt.y = rect.bottom;
- typename std::set<SweepEvent<TId, TNum>>::iterator end_it =
- sweep_v_events.find(v_evt);
- assert(end_it != sweep_v_events.end());
- sweep_v_events.erase(end_it);
- }
-
- // Peeks ahead to see if there are other rectangles sharing a vertical edge
- // with the current sweep line. If so, we want to continue marking up the
- // sweep line before actually processing the rectangles the sweep line is
- // intersecting.
- typename std::set<SweepEvent<TId, TNum>>::iterator next_it = it;
- ++next_it;
- if (next_it != sweep_h_events.end()) {
- if (next_it->x == h_evt.x) {
- continue;
- }
- }
-
-#ifdef RECTS_DEBUG
- std::cout << h_evt.x << std::endl;
-#endif
-
- // After the following for loop, active_regions will be a list of
- // y-coordinates paired with the set of rectangle IDs that are intersect at
- // that y-coordinate (and the current sweep line's x-coordinate). For
- // example if the current sweep line were the left edge of a scene with only
- // one rectangle of ID 0 and bounds (left, top, right, bottom) == (2, 3, 4,
- // 5), active_regions will be [({ 0 }, 3), {}, 5].
- active_regions.clear();
- IdSet<TId> active_set;
- for (typename std::set<SweepEvent<TId, TNum>>::iterator it =
- sweep_v_events.begin();
- it != sweep_v_events.end(); ++it) {
- const SweepEvent<TId, TNum> &v_evt = *it;
-
- if (v_evt.type == START) {
- active_set.add(v_evt.rect_id);
- } else {
- active_set.subtract(v_evt.rect_id);
- }
-
- if (active_regions.size() > 0 && active_regions.back().first == v_evt.y) {
- active_regions.back().second = active_set;
- } else {
- active_regions.push_back(std::make_pair(v_evt.y, active_set));
- }
- }
-
-#ifdef RECTS_DEBUG
- std::cout << "x:" << h_evt.x;
- for (std::vector<std::pair<TNum, IdSet>>::iterator it =
- active_regions.begin();
- it != active_regions.end(); ++it) {
- std::cout << " " << it->first << "(" << it->second << ")"
- << ",";
- }
- std::cout << std::endl;
-#endif
-
- // To determine which started rectangles are ending this event, we make them
- // all as false, or unseen during this sweep line.
- for (typename std::map<StartedRect<TId, TNum>, bool>::iterator it =
- started_rects.begin();
- it != started_rects.end(); ++it) {
- it->second = false;
- }
-
- // This for loop will iterate all potential new rectangles and either
- // discover it was already started (and then mark it true), or that it is a
- // new rectangle and add it to the started rectangles. A started rectangle
- // is unique if it has a distinct top, bottom, and set of rectangle IDs.
- // This is tricky because a potential rectangle could be encountered here
- // that has a non-unique top and bottom, so it shares geometry with an
- // already started rectangle, but the set of rectangle IDs differs. In that
- // case, we have a new rectangle, and the already existing started rectangle
- // will not be marked as seen ("true" in the std::pair) and will get ended
- // by the for loop after this one. This is as intended.
- for (typename std::vector<std::pair<TNum, IdSet<TId>>>::iterator it =
- active_regions.begin();
- it != active_regions.end(); ++it) {
- IdSet<TId> region_set = it->second;
-
- if (region_set.isEmpty())
- continue;
-
- // An important property of active_regions is that each region where a set
- // of rectangles applies is bounded at the bottom by the next (in the
- // vector) region's starting y-coordinate.
- typename std::vector<std::pair<TNum, IdSet<TId>>>::iterator next_it = it;
- ++next_it;
- assert(next_it != active_regions.end());
-
- TNum region_top = it->first;
- TNum region_bottom = next_it->first;
-
- StartedRect<TId, TNum> rect_key;
- rect_key.id_set = region_set;
- rect_key.left = h_evt.x;
- rect_key.top = region_top;
- rect_key.bottom = region_bottom;
-
- // Remember that rect_key.left is ignored for the purposes of searching
- // the started rects. This follows from the fact that a previously started
- // rectangle would by definition have a left bound less than the current
- // event's x-coordinate. We are interested in continuing the started
- // rectangles by marking them seen (true) but we don't know, care, or wish
- // to change the left bound at this point. If there are no matching
- // rectangles for this region, start a new one and mark it as seen (true).
- typename std::map<StartedRect<TId, TNum>, bool>::iterator
- started_rect_it = started_rects.find(rect_key);
- if (started_rect_it == started_rects.end()) {
- started_rects[rect_key] = true;
- } else {
- started_rect_it->second = true;
- }
- }
-
- // This for loop ends all rectangles that were unseen during this event.
- // Because this is the first event where we didn't see this rectangle, it's
- // right edge is exactly the current event's x-coordinate. With this, we
- // have the final piece of information to output this rectangle's geometry
- // and set of input rectangle IDs. To end a started rectangle, we erase it
- // from the started_rects map and append the completed rectangle to the
- // output vector.
- for (typename std::map<StartedRect<TId, TNum>, bool>::iterator it =
- started_rects.begin();
- it != started_rects.end();
- /* inc in body */) {
- if (!it->second) {
- const StartedRect<TId, TNum> &proto_rect = it->first;
- Rect<TNum> out_rect;
- out_rect.left = proto_rect.left;
- out_rect.top = proto_rect.top;
- out_rect.right = h_evt.x;
- out_rect.bottom = proto_rect.bottom;
- out->push_back(RectSet<TId, TNum>(proto_rect.id_set, out_rect));
- started_rects.erase(it++); // Also increments out iterator.
-
-#ifdef RECTS_DEBUG
- std::cout << " <" << proto_rect.id_set << "(" << rect << ")"
- << std::endl;
-#endif
- } else {
- // Remember this for loop has no built in increment step. We do it here.
- ++it;
- }
- }
- }
-}
-
-void separate_frects_64(const std::vector<Rect<float>> &in,
- std::vector<RectSet<uint64_t, float>> *out) {
- separate_rects(in, out);
-}
-
-void separate_rects_64(const std::vector<Rect<int>> &in,
- std::vector<RectSet<uint64_t, int>> *out) {
- separate_rects(in, out);
-}
-
-} // namespace separate_rects
+++ /dev/null
-/*
- * Copyright (C) 2015 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.
- */
-
-#ifndef DRM_HWCOMPOSER_SEPARATE_RECTS_H_
-#define DRM_HWCOMPOSER_SEPARATE_RECTS_H_
-
-#include <stdint.h>
-
-#include <sstream>
-#include <vector>
-
-namespace separate_rects {
-
-template <typename TFloat>
-struct Rect {
- union {
- struct {
- TFloat left, top, right, bottom;
- };
- struct {
- TFloat x1, y1, x2, y2;
- };
- TFloat bounds[4];
- };
-
- typedef TFloat TNum;
-
- Rect() {
- }
-
- Rect(TFloat xx1, TFloat yy1, TFloat xx2, TFloat yy2)
- : x1(xx1), y1(yy1), x2(xx2), y2(yy2) {
- }
-
- template <typename T>
- Rect(const Rect<T> &rhs) {
- for (int i = 0; i < 4; i++)
- bounds[i] = rhs.bounds[i];
- }
-
- template <typename T>
- Rect<TFloat> &operator=(const Rect<T> &rhs) {
- for (int i = 0; i < 4; i++)
- bounds[i] = rhs.bounds[i];
- return *this;
- }
-
- bool operator==(const Rect &rhs) const {
- for (int i = 0; i < 4; i++) {
- if (bounds[i] != rhs.bounds[i])
- return false;
- }
-
- return true;
- }
-
- TFloat width() const {
- return bounds[2] - bounds[0];
- }
-
- TFloat height() const {
- return bounds[3] - bounds[1];
- }
-
- TFloat area() const {
- return width() * height();
- }
-
- void Dump(std::ostringstream *out) const {
- *out << "[x/y/w/h]=" << left << "/" << top << "/" << width() << "/"
- << height();
- }
-};
-
-template <typename TUInt>
-struct IdSet {
- public:
- typedef TUInt TId;
-
- IdSet() : bitset(0) {
- }
-
- IdSet(TId id) : bitset(0) {
- add(id);
- }
-
- void add(TId id) {
- bitset |= ((TUInt)1) << id;
- }
-
- void subtract(TId id) {
- bitset &= ~(((TUInt)1) << id);
- }
-
- bool isEmpty() const {
- return bitset == 0;
- }
-
- TUInt getBits() const {
- return bitset;
- }
-
- bool operator==(const IdSet<TId> &rhs) const {
- return bitset == rhs.bitset;
- }
-
- bool operator<(const IdSet<TId> &rhs) const {
- return bitset < rhs.bitset;
- }
-
- IdSet<TId> operator|(const IdSet<TId> &rhs) const {
- IdSet ret;
- ret.bitset = bitset | rhs.bitset;
- return ret;
- }
-
- IdSet<TId> operator|(TId id) const {
- IdSet<TId> ret;
- ret.bitset = bitset;
- ret.add(id);
- return ret;
- }
-
- static const int max_elements = sizeof(TId) * 8;
-
- private:
- TUInt bitset;
-};
-
-template <typename TId, typename TNum>
-struct RectSet {
- IdSet<TId> id_set;
- Rect<TNum> rect;
-
- RectSet(const IdSet<TId> &i, const Rect<TNum> &r) : id_set(i), rect(r) {
- }
-
- bool operator==(const RectSet<TId, TNum> &rhs) const {
- return id_set == rhs.id_set && rect == rhs.rect;
- }
-};
-
-// Separates up to a maximum of 64 input rectangles into mutually non-
-// overlapping rectangles that cover the exact same area and outputs those non-
-// overlapping rectangles. Each output rectangle also includes the set of input
-// rectangle indices that overlap the output rectangle encoded in a bitset. For
-// example, an output rectangle that overlaps input rectangles in[0], in[1], and
-// in[4], the bitset would be (ommitting leading zeroes) 10011.
-void separate_frects_64(const std::vector<Rect<float>> &in,
- std::vector<RectSet<uint64_t, float>> *out);
-void separate_rects_64(const std::vector<Rect<int>> &in,
- std::vector<RectSet<uint64_t, int>> *out);
-
-} // namespace separate_rects
-
-#endif
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
- separate_rects_test.cpp \
worker_test.cpp
LOCAL_MODULE := hwc-drm-tests
+++ /dev/null
-#include <gtest/gtest.h>
-#include <hardware/hardware.h>
-
-#include "separate_rects.h"
-
-using namespace separate_rects;
-
-#define RectSet RectSet<TId, TNum>
-#define Rect Rect<TNum>
-#define IdSet IdSet<TId>
-typedef uint64_t TId;
-typedef float TNum;
-
-struct SeparateRectTest : public testing::Test {
- bool IsEquality(std::vector<RectSet> &out,
- std::vector<RectSet> &expected_out) {
- // Test for rects missing from out
- for (size_t i = 0; i < expected_out.size(); i++) {
- RectSet &ex_out = expected_out[i];
- if (std::find(out.begin(), out.end(), ex_out) == out.end()) {
- return false;
- }
- }
-
- // Test for presence of unexpected rects in out
- for (size_t i = 0; i < out.size(); i++) {
- RectSet &actual_out = out[i];
- if (std::find(expected_out.begin(), expected_out.end(), actual_out) ==
- expected_out.end()) {
- return false;
- }
- }
-
- return true;
- }
-};
-
-TEST_F(SeparateRectTest, test_separate_rect) {
- std::vector<Rect> in;
- std::vector<RectSet> out;
- std::vector<RectSet> expected_out;
-
- in.push_back({0, 0, 4, 5});
- in.push_back({2, 0, 6, 6});
- in.push_back({4, 0, 8, 5});
- in.push_back({0, 7, 8, 9});
-
- in.push_back({10, 0, 18, 5});
- in.push_back({12, 0, 16, 5});
-
- in.push_back({20, 11, 24, 17});
- in.push_back({22, 13, 26, 21});
- in.push_back({32, 33, 36, 37});
- in.push_back({30, 31, 38, 39});
-
- in.push_back({40, 43, 48, 45});
- in.push_back({44, 41, 46, 47});
-
- in.push_back({50, 51, 52, 53});
- in.push_back({50, 51, 52, 53});
- in.push_back({50, 51, 52, 53});
-
- in.push_back({0, 0, 0, 10});
- in.push_back({0, 0, 10, 0});
- in.push_back({10, 0, 0, 10});
- in.push_back({0, 10, 10, 0});
-
- for (int i = 0; i < 100000; i++) {
- out.clear();
- separate_frects_64(in, &out);
- }
-
- expected_out.push_back(RectSet(IdSet(0), Rect(0, 0, 2, 5)));
- expected_out.push_back(RectSet(IdSet(1), Rect(2, 5, 6, 6)));
- expected_out.push_back(RectSet(IdSet(1) | 0, Rect(2, 0, 4, 5)));
- expected_out.push_back(RectSet(IdSet(1) | 2, Rect(4, 0, 6, 5)));
- expected_out.push_back(RectSet(IdSet(2), Rect(6, 0, 8, 5)));
- expected_out.push_back(RectSet(IdSet(3), Rect(0, 7, 8, 9)));
- expected_out.push_back(RectSet(IdSet(4), Rect(10, 0, 12, 5)));
- expected_out.push_back(RectSet(IdSet(5) | 4, Rect(12, 0, 16, 5)));
- expected_out.push_back(RectSet(IdSet(4), Rect(16, 0, 18, 5)));
- expected_out.push_back(RectSet(IdSet(6), Rect(20, 11, 22, 17)));
- expected_out.push_back(RectSet(IdSet(6) | 7, Rect(22, 13, 24, 17)));
- expected_out.push_back(RectSet(IdSet(6), Rect(22, 11, 24, 13)));
- expected_out.push_back(RectSet(IdSet(7), Rect(22, 17, 24, 21)));
- expected_out.push_back(RectSet(IdSet(7), Rect(24, 13, 26, 21)));
- expected_out.push_back(RectSet(IdSet(9), Rect(30, 31, 32, 39)));
- expected_out.push_back(RectSet(IdSet(8) | 9, Rect(32, 33, 36, 37)));
- expected_out.push_back(RectSet(IdSet(9), Rect(32, 37, 36, 39)));
- expected_out.push_back(RectSet(IdSet(9), Rect(32, 31, 36, 33)));
- expected_out.push_back(RectSet(IdSet(9), Rect(36, 31, 38, 39)));
- expected_out.push_back(RectSet(IdSet(10), Rect(40, 43, 44, 45)));
- expected_out.push_back(RectSet(IdSet(10) | 11, Rect(44, 43, 46, 45)));
- expected_out.push_back(RectSet(IdSet(11), Rect(44, 41, 46, 43)));
- expected_out.push_back(RectSet(IdSet(11), Rect(44, 45, 46, 47)));
- expected_out.push_back(RectSet(IdSet(10), Rect(46, 43, 48, 45)));
- expected_out.push_back(RectSet(IdSet(12) | 13 | 14, Rect(50, 51, 52, 53)));
-
- ASSERT_TRUE(IsEquality(out, expected_out));
-}