From 4743831d128dfad4ac9fbafa6e7544dbe7fb7ed2 Mon Sep 17 00:00:00 2001 From: Gabriel de Dietrich Date: Fri, 23 Apr 2010 10:22:44 +0200 Subject: QTabBar: Widgets inside the tab bar where not properly laid out after moveTab() Only the leftmost tab was being correctly laid out after the move due to a mistake in QTabBarPrivate::layoutTab(). Auto-test included. Reviewed-by: Thierry Task-number: QTBUG-10052 --- src/gui/widgets/qtabbar.cpp | 15 +++++---------- src/gui/widgets/qtabbar_p.h | 2 +- tests/auto/qtabbar/tst_qtabbar.cpp | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/gui/widgets/qtabbar.cpp b/src/gui/widgets/qtabbar.cpp index 7559311..d03a2f4 100644 --- a/src/gui/widgets/qtabbar.cpp +++ b/src/gui/widgets/qtabbar.cpp @@ -580,16 +580,10 @@ void QTabBarPrivate::layoutTab(int index) } } -void QTabBarPrivate::layoutWidgets(int index) +void QTabBarPrivate::layoutWidgets(int start) { Q_Q(QTabBar); - int start = 0; - int end = q->count(); - if (index != -1) { - start = qMax(index, 0); - end = qMin(end, start + 1); - } - for (int i = start; i < end; ++i) { + for (int i = start; i < q->count(); ++i) { layoutTab(i); } } @@ -1171,8 +1165,9 @@ void QTabBar::setCurrentIndex(int index) update(); d->makeVisible(index); d->tabList[index].lastTab = oldIndex; - d->layoutWidgets(oldIndex); - d->layoutWidgets(index); + if (oldIndex >= 0 && oldIndex < count()) + d->layoutTab(oldIndex); + d->layoutTab(index); #ifdef QT3_SUPPORT emit selected(index); #endif diff --git a/src/gui/widgets/qtabbar_p.h b/src/gui/widgets/qtabbar_p.h index 83636e6..37741f7 100644 --- a/src/gui/widgets/qtabbar_p.h +++ b/src/gui/widgets/qtabbar_p.h @@ -178,7 +178,7 @@ public: void refresh(); void layoutTabs(); - void layoutWidgets(int index = -1); + void layoutWidgets(int start = 0); void layoutTab(int index); void updateMacBorderMetrics(); void setupMovableTab(); diff --git a/tests/auto/qtabbar/tst_qtabbar.cpp b/tests/auto/qtabbar/tst_qtabbar.cpp index 72f9dd3..ac3de20 100644 --- a/tests/auto/qtabbar/tst_qtabbar.cpp +++ b/tests/auto/qtabbar/tst_qtabbar.cpp @@ -95,6 +95,8 @@ private slots: void task251184_removeTab(); void changeTitleWhileDoubleClickingTab(); + + void taskQTBUG_10052_widgetLayoutWhenMoving(); }; // Testing get/set functions @@ -576,5 +578,38 @@ void tst_QTabBar::changeTitleWhileDoubleClickingTab() QTest::mouseDClick(&bar, Qt::LeftButton, 0, tabPos); } +class Widget10052 : public QWidget +{ +public: + Widget10052(QWidget *parent) : QWidget(parent), moved(false) + { } + + void moveEvent(QMoveEvent *e) + { + moved = e->oldPos() != e->pos(); + QWidget::moveEvent(e); + } + + bool moved; +}; + +void tst_QTabBar::taskQTBUG_10052_widgetLayoutWhenMoving() +{ + QTabBar tabBar; + tabBar.insertTab(0, "My first tab"); + Widget10052 w1(&tabBar); + tabBar.setTabButton(0, QTabBar::RightSide, &w1); + tabBar.insertTab(1, "My other tab"); + Widget10052 w2(&tabBar); + tabBar.setTabButton(1, QTabBar::RightSide, &w2); + + tabBar.show(); + QTest::qWaitForWindowShown(&tabBar); + w1.moved = w2.moved = false; + tabBar.moveTab(0, 1); + QTRY_VERIFY(w1.moved); + QVERIFY(w2.moved); +} + QTEST_MAIN(tst_QTabBar) #include "tst_qtabbar.moc" -- cgit v0.12 From c1431e6daa5f0e229c2e79a945f28d00d70b0978 Mon Sep 17 00:00:00 2001 From: aavit Date: Fri, 9 Apr 2010 15:30:04 +0200 Subject: Optimization: Avoid data copy when reading jpegs from memory If the iodevice is actually a qbuffer, avoid read() and just give libjpeg a pointer to the contained data directly. Related to QTBUG-9095. Reviewed-by: Kim --- src/plugins/imageformats/jpeg/qjpeghandler.cpp | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/plugins/imageformats/jpeg/qjpeghandler.cpp b/src/plugins/imageformats/jpeg/qjpeghandler.cpp index 93b7cc6..1c6a289 100644 --- a/src/plugins/imageformats/jpeg/qjpeghandler.cpp +++ b/src/plugins/imageformats/jpeg/qjpeghandler.cpp @@ -44,6 +44,7 @@ #include #include #include +#include #include // jpeglib needs this to be pre-included #include @@ -102,6 +103,7 @@ struct my_jpeg_source_mgr : public jpeg_source_mgr { // Nothing dynamic - cannot rely on destruction over longjump QIODevice *device; JOCTET buffer[max_buf]; + const QBuffer *memDevice; public: my_jpeg_source_mgr(QIODevice *device); @@ -117,10 +119,14 @@ static void qt_init_source(j_decompress_ptr) static boolean qt_fill_input_buffer(j_decompress_ptr cinfo) { - int num_read; my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src; + if (src->memDevice) { + src->next_input_byte = (const JOCTET *)src->memDevice->data().constData(); + src->bytes_in_buffer = (size_t)src->memDevice->data().size(); + return true; + } src->next_input_byte = src->buffer; - num_read = src->device->read((char*)src->buffer, max_buf); + int num_read = src->device->read((char*)src->buffer, max_buf); if (num_read <= 0) { // Insert a fake EOI marker - as per jpeglib recommendation src->buffer[0] = (JOCTET) 0xFF; @@ -147,7 +153,7 @@ static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes) * any trouble anyway --- large skips are infrequent. */ if (num_bytes > 0) { - while (num_bytes > (long) src->bytes_in_buffer) { + while (num_bytes > (long) src->bytes_in_buffer) { // Should not happen in case of memDevice num_bytes -= (long) src->bytes_in_buffer; (void) qt_fill_input_buffer(cinfo); /* note we assume that qt_fill_input_buffer will never return false, @@ -178,6 +184,7 @@ inline my_jpeg_source_mgr::my_jpeg_source_mgr(QIODevice *device) jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart; jpeg_source_mgr::term_source = qt_term_source; this->device = device; + memDevice = qobject_cast(device); bytes_in_buffer = 0; next_input_byte = buffer; } -- cgit v0.12 From ffb5db8aafbe2b49be5cd454841a2af8acc426ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Samuel=20R=C3=B8dal?= Date: Fri, 23 Apr 2010 10:51:31 +0200 Subject: Avoided O(n^2) behavior in painter path clipper. Use a binary tree when producing the line vs line intersections to get O(n * log n) behavior instead. Also tweak the threshold a bit to avoid tessellating the bezier curves too finely. Reviewed-by: Gunnar Sletta --- src/gui/painting/qpathclipper.cpp | 344 ++++++++++++++++++++++++++++++-------- 1 file changed, 276 insertions(+), 68 deletions(-) diff --git a/src/gui/painting/qpathclipper.cpp b/src/gui/painting/qpathclipper.cpp index 9dedf3a..c910024 100644 --- a/src/gui/painting/qpathclipper.cpp +++ b/src/gui/painting/qpathclipper.cpp @@ -43,6 +43,7 @@ #include #include +#include #include /** @@ -105,7 +106,6 @@ public: bool hasIntersections(const QPathSegments &a, const QPathSegments &b) const; private: - void intersectLines(const QLineF &a, const QLineF &b, QDataBuffer &intersections); bool linesIntersect(const QLineF &a, const QLineF &b) const; }; @@ -176,7 +176,223 @@ bool QIntersectionFinder::linesIntersect(const QLineF &a, const QLineF &b) const return tq >= 0 && tq <= 1; } -void QIntersectionFinder::intersectLines(const QLineF &a, const QLineF &b, QDataBuffer &intersections) +bool QIntersectionFinder::hasIntersections(const QPathSegments &a, const QPathSegments &b) const +{ + if (a.segments() == 0 || b.segments() == 0) + return false; + + const QRectF &rb0 = b.elementBounds(0); + + qreal minX = rb0.left(); + qreal minY = rb0.top(); + qreal maxX = rb0.right(); + qreal maxY = rb0.bottom(); + + for (int i = 1; i < b.segments(); ++i) { + const QRectF &r = b.elementBounds(i); + minX = qMin(minX, r.left()); + minY = qMin(minY, r.top()); + maxX = qMax(maxX, r.right()); + maxY = qMax(maxY, r.bottom()); + } + + QRectF rb(minX, minY, maxX - minX, maxY - minY); + + for (int i = 0; i < a.segments(); ++i) { + const QRectF &r1 = a.elementBounds(i); + + if (r1.left() > rb.right() || rb.left() > r1.right()) + continue; + if (r1.top() > rb.bottom() || rb.top() > r1.bottom()) + continue; + + for (int j = 0; j < b.segments(); ++j) { + const QRectF &r2 = b.elementBounds(j); + + if (r1.left() > r2.right() || r2.left() > r1.right()) + continue; + if (r1.top() > r2.bottom() || r2.top() > r1.bottom()) + continue; + + if (linesIntersect(a.lineAt(i), b.lineAt(j))) + return true; + } + } + + return false; +} + +namespace { +struct TreeNode +{ + qreal splitLeft; + qreal splitRight; + bool leaf; + + int lowestLeftIndex; + int lowestRightIndex; + + union { + struct { + int first; + int last; + } interval; + struct { + int left; + int right; + } children; + } index; +}; + +struct RectF +{ + qreal x1; + qreal y1; + qreal x2; + qreal y2; +}; + +class SegmentTree +{ +public: + SegmentTree(QPathSegments &segments); + + QRectF boundingRect() const; + + void produceIntersections(int segment); + +private: + TreeNode buildTree(int first, int last, int depth, const RectF &bounds); + + void produceIntersectionsLeaf(const TreeNode &node, int segment); + void produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis); + void intersectLines(const QLineF &a, const QLineF &b, QDataBuffer &intersections); + + QPathSegments &m_segments; + QVector m_index; + + RectF m_bounds; + + QVector m_tree; + QDataBuffer m_intersections; +}; + +SegmentTree::SegmentTree(QPathSegments &segments) + : m_segments(segments) +{ + m_bounds.x1 = qt_inf(); + m_bounds.y1 = qt_inf(); + m_bounds.x2 = -qt_inf(); + m_bounds.y2 = -qt_inf(); + + m_index.resize(m_segments.segments()); + + for (int i = 0; i < m_index.size(); ++i) { + m_index[i] = i; + + const QRectF &segmentBounds = m_segments.elementBounds(i); + + if (segmentBounds.left() < m_bounds.x1) + m_bounds.x1 = segmentBounds.left(); + if (segmentBounds.top() < m_bounds.y1) + m_bounds.y1 = segmentBounds.top(); + if (segmentBounds.right() > m_bounds.x2) + m_bounds.x2 = segmentBounds.right(); + if (segmentBounds.bottom() > m_bounds.y2) + m_bounds.y2 = segmentBounds.bottom(); + } + + m_tree.resize(1); + + TreeNode root = buildTree(0, m_index.size(), 0, m_bounds); + m_tree[0] = root; +} + +QRectF SegmentTree::boundingRect() const +{ + return QRectF(QPointF(m_bounds.x1, m_bounds.y1), + QPointF(m_bounds.x2, m_bounds.y2)); +} + +static inline qreal coordinate(const QPointF &pos, int axis) +{ + return axis == 0 ? pos.x() : pos.y(); +} + +TreeNode SegmentTree::buildTree(int first, int last, int depth, const RectF &bounds) +{ + if (depth >= 24 || (last - first) <= 10) { + TreeNode node; + node.leaf = true; + node.index.interval.first = first; + node.index.interval.last = last; + + return node; + } + + int splitAxis = (depth & 1); + + TreeNode node; + node.leaf = false; + + qreal split = 0.5f * ((&bounds.x1)[splitAxis] + (&bounds.x2)[splitAxis]); + + node.splitLeft = (&bounds.x1)[splitAxis]; + node.splitRight = (&bounds.x2)[splitAxis]; + + node.lowestLeftIndex = INT_MAX; + node.lowestRightIndex = INT_MAX; + + const int treeSize = m_tree.size(); + + node.index.children.left = treeSize; + node.index.children.right = treeSize + 1; + + m_tree.resize(treeSize + 2); + + int l = first; + int r = last - 1; + + // partition into left and right sets + while (l <= r) { + const int index = m_index.at(l); + const QRectF &segmentBounds = m_segments.elementBounds(index); + + qreal lowCoordinate = coordinate(segmentBounds.topLeft(), splitAxis); + + if (coordinate(segmentBounds.center(), splitAxis) < split) { + qreal highCoordinate = coordinate(segmentBounds.bottomRight(), splitAxis); + if (highCoordinate > node.splitLeft) + node.splitLeft = highCoordinate; + if (index < node.lowestLeftIndex) + node.lowestLeftIndex = index; + ++l; + } else { + if (lowCoordinate < node.splitRight) + node.splitRight = lowCoordinate; + if (index < node.lowestRightIndex) + node.lowestRightIndex = index; + qSwap(m_index[l], m_index[r]); + --r; + } + } + + RectF lbounds = bounds; + (&lbounds.x2)[splitAxis] = node.splitLeft; + + RectF rbounds = bounds; + (&rbounds.x1)[splitAxis] = node.splitRight; + + TreeNode left = buildTree(first, l, depth + 1, lbounds); + m_tree[node.index.children.left] = left; + + TreeNode right = buildTree(l, last, depth + 1, rbounds); + m_tree[node.index.children.right] = right; + + return node; +} + +void SegmentTree::intersectLines(const QLineF &a, const QLineF &b, QDataBuffer &intersections) { const QPointF p1 = a.p1(); const QPointF p2 = a.p2(); @@ -298,90 +514,86 @@ void QIntersectionFinder::intersectLines(const QLineF &a, const QLineF &b, QData intersections.add(intersection); } -bool QIntersectionFinder::hasIntersections(const QPathSegments &a, const QPathSegments &b) const +void SegmentTree::produceIntersections(int segment) { - if (a.segments() == 0 || b.segments() == 0) - return false; + const QRectF &segmentBounds = m_segments.elementBounds(segment); - const QRectF &rb0 = b.elementBounds(0); + RectF sbounds; + sbounds.x1 = segmentBounds.left(); + sbounds.y1 = segmentBounds.top(); + sbounds.x2 = segmentBounds.right(); + sbounds.y2 = segmentBounds.bottom(); - qreal minX = rb0.left(); - qreal minY = rb0.top(); - qreal maxX = rb0.right(); - qreal maxY = rb0.bottom(); + produceIntersections(m_tree.at(0), segment, sbounds, m_bounds, 0); +} - for (int i = 1; i < b.segments(); ++i) { - const QRectF &r = b.elementBounds(i); - minX = qMin(minX, r.left()); - minY = qMin(minY, r.top()); - maxX = qMax(maxX, r.right()); - maxY = qMax(maxY, r.bottom()); - } +void SegmentTree::produceIntersectionsLeaf(const TreeNode &node, int segment) +{ + const QRectF &r1 = m_segments.elementBounds(segment); + const QLineF lineA = m_segments.lineAt(segment); - QRectF rb(minX, minY, maxX - minX, maxY - minY); + for (int i = node.index.interval.first; i < node.index.interval.last; ++i) { + const int other = m_index.at(i); + if (other >= segment) + continue; - for (int i = 0; i < a.segments(); ++i) { - const QRectF &r1 = a.elementBounds(i); + const QRectF &r2 = m_segments.elementBounds(other); - if (r1.left() > rb.right() || rb.left() > r1.right()) + if (r1.left() > r2.right() || r2.left() > r1.right()) continue; - if (r1.top() > rb.bottom() || rb.top() > r1.bottom()) + if (r1.top() > r2.bottom() || r2.top() > r1.bottom()) continue; - for (int j = 0; j < b.segments(); ++j) { - const QRectF &r2 = b.elementBounds(j); + m_intersections.reset(); - if (r1.left() > r2.right() || r2.left() > r1.right()) - continue; - if (r1.top() > r2.bottom() || r2.top() > r1.bottom()) - continue; + const QLineF lineB = m_segments.lineAt(other); - if (linesIntersect(a.lineAt(i), b.lineAt(j))) - return true; - } - } + intersectLines(lineA, lineB, m_intersections); - return false; -} + for (int k = 0; k < m_intersections.size(); ++k) { + QPathSegments::Intersection i_isect, j_isect; + i_isect.vertex = j_isect.vertex = m_segments.addPoint(m_intersections.at(k).pos); -void QIntersectionFinder::produceIntersections(QPathSegments &segments) -{ - QVector > t; - QDataBuffer intersections; + i_isect.t = m_intersections.at(k).alphaA; + j_isect.t = m_intersections.at(k).alphaB; - for (int i = 0; i < segments.segments(); ++i) { - const QRectF &r1 = segments.elementBounds(i); + i_isect.next = 0; + j_isect.next = 0; - for (int j = 0; j < i; ++j) { - const QRectF &r2 = segments.elementBounds(j); + m_segments.addIntersection(segment, i_isect); + m_segments.addIntersection(other, j_isect); + } + } +} - if (r1.left() > r2.right() || r2.left() > r1.right()) - continue; - if (r1.top() > r2.bottom() || r2.top() > r1.bottom()) - continue; +void SegmentTree::produceIntersections(const TreeNode &node, int segment, const RectF &segmentBounds, const RectF &nodeBounds, int axis) +{ + if (node.leaf) { + produceIntersectionsLeaf(node, segment); + return; + } - intersections.reset(); + RectF lbounds = nodeBounds; + (&lbounds.x2)[axis] = node.splitLeft; - const QLineF lineA = segments.lineAt(i); - const QLineF lineB = segments.lineAt(j); + RectF rbounds = nodeBounds; + (&rbounds.x1)[axis] = node.splitRight; - intersectLines(lineA, lineB, intersections); + if (segment > node.lowestLeftIndex && (&segmentBounds.x1)[axis] <= node.splitLeft) + produceIntersections(m_tree.at(node.index.children.left), segment, segmentBounds, lbounds, !axis); - for (int k = 0; k < intersections.size(); ++k) { - QPathSegments::Intersection i_isect, j_isect; - i_isect.vertex = j_isect.vertex = segments.addPoint(intersections.at(k).pos); + if (segment > node.lowestRightIndex && (&segmentBounds.x2)[axis] >= node.splitRight) + produceIntersections(m_tree.at(node.index.children.right), segment, segmentBounds, rbounds, !axis); +} - i_isect.t = intersections.at(k).alphaA; - j_isect.t = intersections.at(k).alphaB; +} - i_isect.next = 0; - j_isect.next = 0; +void QIntersectionFinder::produceIntersections(QPathSegments &segments) +{ + SegmentTree tree(segments); - segments.addIntersection(i, i_isect); - segments.addIntersection(j, j_isect); - } - } - } + for (int i = 0; i < segments.segments(); ++i) + tree.produceIntersections(i); } class QKdPointTree @@ -707,9 +919,6 @@ void QPathSegments::addPath(const QPainterPath &path) { int firstSegment = m_segments.size(); - QRectF pathBounds = path.boundingRect(); - qreal invPathScale = 1 / qMax(pathBounds.width(), pathBounds.height()); - bool hasMoveTo = false; int lastMoveTo = 0; int last = 0; @@ -746,10 +955,9 @@ void QPathSegments::addPath(const QPainterPath &path) } else { QRectF bounds = bezier.bounds(); - qreal segmentScale = qMax(bounds.width(), bounds.height()); + // threshold based on similar algorithm as in qtriangulatingstroker.cpp + int threshold = qMin(64, qMax(bounds.width(), bounds.height()) * (2 * qreal(3.14) / 6)); - // threshold based on same algorithm as in qtriangulatingstroker.cpp - int threshold = qMin(64, segmentScale * invPathScale * (512 * 3.14f / 6)); if (threshold < 3) threshold = 3; qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1); -- cgit v0.12