source: trunk/src/gui/painting/qwindowsurface_raster.cpp@ 347

Last change on this file since 347 was 308, checked in by Dmitry A. Kuminov, 16 years ago

gui: Use manual image flipping (from Qt to PM) instead of setting the world transformation matrix. Added a simple pixel/ms counter for measurements.

File size: 17.7 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include <qdebug.h>
43
44#include <qglobal.h> // for Q_WS_WIN define (non-PCH)
45#ifdef Q_WS_WIN
46#include <qt_windows.h>
47#endif
48
49#include <QtGui/qpaintdevice.h>
50#include <QtGui/qwidget.h>
51
52#include "private/qwindowsurface_raster_p.h"
53#include "private/qnativeimage_p.h"
54#include "private/qwidget_p.h"
55
56#ifdef Q_WS_X11
57#include "private/qpixmap_x11_p.h"
58#include "private/qt_x11_p.h"
59#include "private/qwidget_p.h"
60#include "qx11info_x11.h"
61#endif
62#include "private/qdrawhelper_p.h"
63
64#ifdef Q_WS_MAC
65#include <private/qt_cocoa_helpers_mac_p.h>
66#endif
67
68#if defined(Q_WS_PM) && defined(QT_LOG_BLITSPEED)
69#include <InnotekLIBC/FastInfoBlocks.h>
70#endif
71
72QT_BEGIN_NAMESPACE
73
74#ifdef Q_WS_WIN
75PtrUpdateLayeredWindowIndirect ptrUpdateLayeredWindowIndirect;
76#endif
77
78#if defined(Q_WS_PM) && defined(QT_LOG_BLITSPEED)
79unsigned long long qt_total_blit_ms = 0;
80unsigned long long qt_total_blit_pixels = 0;
81#endif
82
83class QRasterWindowSurfacePrivate
84{
85public:
86 QNativeImage *image;
87
88#ifdef Q_WS_X11
89 GC gc;
90#ifndef QT_NO_XRENDER
91 uint translucentBackground : 1;
92#endif
93#endif
94#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
95 uint canUseLayeredWindow : 1;
96#endif
97 uint inSetGeometry : 1;
98};
99
100QRasterWindowSurface::QRasterWindowSurface(QWidget *window)
101 : QWindowSurface(window), d_ptr(new QRasterWindowSurfacePrivate)
102{
103#ifdef Q_WS_X11
104 d_ptr->gc = XCreateGC(X11->display, window->handle(), 0, 0);
105#ifndef QT_NO_XRENDER
106 d_ptr->translucentBackground = X11->use_xrender
107 && window->x11Info().depth() == 32;
108#endif
109#endif
110#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
111 d_ptr->canUseLayeredWindow = ptrUpdateLayeredWindowIndirect
112 && (qt_widget_private(window)->data.window_flags & Qt::FramelessWindowHint);
113#endif
114 d_ptr->image = 0;
115 d_ptr->inSetGeometry = false;
116 setStaticContentsSupport(true);
117}
118
119
120QRasterWindowSurface::~QRasterWindowSurface()
121{
122#ifdef Q_WS_X11
123 XFreeGC(X11->display, d_ptr->gc);
124#endif
125 if (d_ptr->image)
126 delete d_ptr->image;
127
128 delete d_ptr;
129}
130
131
132QPaintDevice *QRasterWindowSurface::paintDevice()
133{
134 return &d_ptr->image->image;
135}
136
137void QRasterWindowSurface::beginPaint(const QRegion &rgn)
138{
139#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_OS_WINCE))
140 if (!qt_widget_private(window())->isOpaque) {
141#if defined(Q_WS_WIN) && !defined(Q_OS_WINCE)
142 if (d_ptr->image->image.format() != QImage::Format_ARGB32_Premultiplied
143 && d_ptr->canUseLayeredWindow)
144 prepareBuffer(QImage::Format_ARGB32_Premultiplied, window());
145#endif
146 QPainter p(&d_ptr->image->image);
147 p.setCompositionMode(QPainter::CompositionMode_Source);
148 const QVector<QRect> rects = rgn.rects();
149 const QColor blank = Qt::transparent;
150 for (QVector<QRect>::const_iterator it = rects.begin(); it != rects.end(); ++it) {
151 p.fillRect(*it, blank);
152 }
153 }
154#endif
155#if defined(Q_OS_WINCE)
156 Q_UNUSED(rgn);
157#endif
158}
159
160void QRasterWindowSurface::flush(QWidget *widget, const QRegion &rgn, const QPoint &offset)
161{
162 Q_D(QRasterWindowSurface);
163
164 // Not ready for painting yet, bail out. This can happen in
165 // QWidget::create_sys()
166 if (!d->image)
167 return;
168
169#ifdef Q_WS_WIN
170 QRect br = rgn.boundingRect();
171
172#ifndef Q_OS_WINCE
173 if (!qt_widget_private(window())->isOpaque && d->canUseLayeredWindow) {
174 QRect r = window()->frameGeometry();
175 QPoint frameOffset = qt_widget_private(window())->frameStrut().topLeft();
176 QRect dirtyRect = br.translated(offset + frameOffset);
177
178 SIZE size = {r.width(), r.height()};
179 POINT ptDst = {r.x(), r.y()};
180 POINT ptSrc = {0, 0};
181 Q_BLENDFUNCTION blend = {AC_SRC_OVER, 0, (int)(255.0 * window()->windowOpacity()), Q_AC_SRC_ALPHA};
182 RECT dirty = {dirtyRect.x(), dirtyRect.y(),
183 dirtyRect.x() + dirtyRect.width(), dirtyRect.y() + dirtyRect.height()};
184 Q_UPDATELAYEREDWINDOWINFO info = {sizeof(info), NULL, &ptDst, &size, d->image->hdc, &ptSrc, 0, &blend, Q_ULW_ALPHA, &dirty};
185
186 (*ptrUpdateLayeredWindowIndirect)(window()->internalWinId(), &info);
187 } else
188#endif
189 {
190 QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
191
192 HDC widget_dc = widget->getDC();
193
194 QRect wbr = br.translated(-wOffset);
195 BitBlt(widget_dc, wbr.x(), wbr.y(), wbr.width(), wbr.height(),
196 d->image->hdc, br.x() + offset.x(), br.y() + offset.y(), SRCCOPY);
197 widget->releaseDC(widget_dc);
198 }
199
200#ifndef QT_NO_DEBUG
201 static bool flush = !qgetenv("QT_FLUSH_WINDOWSURFACE").isEmpty();
202 if (flush) {
203 SelectObject(qt_win_display_dc(), GetStockObject(BLACK_BRUSH));
204 Rectangle(qt_win_display_dc(), 0, 0, d->image->width() + 2, d->image->height() + 2);
205 BitBlt(qt_win_display_dc(), 1, 1, d->image->width(), d->image->height(),
206 d->image->hdc, 0, 0, SRCCOPY);
207 }
208#endif
209
210#endif
211
212#ifdef Q_WS_PM
213 QRect br = rgn.boundingRect();
214
215 HPS wps = widget->getPS();
216
217#if 0
218 qDebug("QRasterWindowSurface::flush: [%s] wps %x br %d,%d/%d,%d",
219 qWidgetName(widget).toUtf8().constData(), (unsigned int)wps,
220 br.x(), br.y(), br.width(), br.height());
221#endif
222
223#ifdef QT_LOG_BLITSPEED
224 unsigned long ticks = fibGetMsCount();
225#endif
226
227#if 1
228 // flip the image vertically for PM
229 QImage img = d->image->image.mirrored();
230
231 QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
232 QRect wbr = br.translated(-wOffset);
233
234 br.translate(offset);
235
236 BITMAPINFOHEADER2 bmh;
237 memset(&bmh, 0, sizeof(BITMAPINFOHEADER2));
238 bmh.cbFix = sizeof(BITMAPINFOHEADER2);
239 bmh.cPlanes = 1;
240 bmh.cBitCount = 32; // @todo support 8-bit indexed colors?
241 bmh.cx = img.width();
242 bmh.cy = img.height();
243
244 int wh = widget->height();
245 int ih = img.height();
246
247 // Note: target is inclusive-inclusive, source is inclusive-exclusive
248 POINTL ptls[] = { { wbr.left(), wh - wbr.bottom() - 1 },
249 { wbr.right(), wh - wbr.top() - 1 },
250 { br.left(), ih - br.bottom() - 1 },
251 { br.right() + 1, ih - br.top() } };
252#if 0
253 qDebug("QRasterWindowSurface::flush: [%s] ptls %ld,%ld-%ld,%ld %ld,%ld-%ld,%ld",
254 qWidgetName(widget).toUtf8().constData(),
255 ptls[0].x, ptls[0].y, ptls[1].x, ptls[1].y,
256 ptls[2].x, ptls[2].y, ptls[3].x, ptls[3].y);
257#endif
258 GpiDrawBits(wps, (PVOID) img.bits(), (PBITMAPINFO2) &bmh, 4, ptls,
259 ROP_SRCCOPY, BBO_IGNORE);
260#else
261 // use the reflection + transformation matrix to flip the y axis.
262 // This should be slower than flipping the image ourselves (unless this
263 // particular matrix is recognized by the driver which then enters some
264 // special mode that doesn't require any flipping at all but uses the
265 // image as is) hence disabled for now.
266 // @todo check if it's really slower than flipping the image bits instead
267 // @todo I guess we can use DIVE here; check it too
268 MATRIXLF m;
269 m.fxM11 = MAKEFIXED(1, 0);
270 m.fxM12 = 0;
271 m.lM13 = 0;
272 m.fxM21 = 0;
273 m.fxM22 = MAKEFIXED(-1, 0);
274 m.lM23 = 0;
275 m.lM31 = 0;
276 m.lM32 = widget->height() - 1;
277 GpiSetDefaultViewMatrix(wps, 8, &m, TRANSFORM_REPLACE);
278
279 QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
280 QRect wbr = br.translated(-wOffset);
281
282 br.translate(offset);
283
284 BITMAPINFOHEADER2 bmh;
285 memset(&bmh, 0, sizeof(BITMAPINFOHEADER2));
286 bmh.cbFix = sizeof(BITMAPINFOHEADER2);
287 bmh.cPlanes = 1;
288 bmh.cBitCount = 32; // @todo support 8-bit indexed colors?
289 bmh.cx = d->image->width();
290 bmh.cy = d->image->height();
291
292 // Note: target is inclusive-inclusive, source is inclusive-exclusive
293 POINTL ptls[] = { { wbr.left(), wbr.top() },
294 { wbr.right(), wbr.bottom() },
295 { br.left(), br.top() },
296 { br.right() + 1, br.bottom() + 1 } };
297 GpiDrawBits(wps, (PVOID) d->image->image.bits(), (PBITMAPINFO2) &bmh, 4, ptls,
298 ROP_SRCCOPY, BBO_IGNORE);
299
300#ifndef QT_NO_DEBUG
301 // for debug flushing
302 const QImage img = d->image->image;
303#endif
304#endif
305
306 widget->releasePS(wps);
307
308#ifdef QT_LOG_BLITSPEED
309 ticks = fibGetMsCount() - ticks;
310 qt_total_blit_ms += ticks;
311 qt_total_blit_pixels += br.width() * br.height();
312#endif
313
314#ifndef QT_NO_DEBUG
315 static bool flush = !qgetenv("QT_FLUSH_WINDOWSURFACE").isEmpty();
316 if (flush) {
317 HPS dps = qt_display_ps();
318 RECTL rcl = { 10, 50, 10 + img.width() + 2, 50 + img.height() + 2 };
319 WinDrawBorder(dps, &rcl, 1, 1, CLR_BLACK, CLR_BLACK, 0);
320 POINTL ptls[] = { { 11, 51, },
321 { 11 + img.width() - 1, 51 + img.height() - 1 },
322 { 0, 0 },
323 { img.width(), img.height() } };
324 GpiDrawBits(dps, (PVOID) img.bits(), (PBITMAPINFO2) &bmh, 4, ptls,
325 ROP_SRCCOPY, BBO_IGNORE);
326 }
327#endif
328
329#endif // Q_WS_PM
330
331#ifdef Q_WS_X11
332 extern void *qt_getClipRects(const QRegion &r, int &num); // in qpaintengine_x11.cpp
333 extern QWidgetData* qt_widget_data(QWidget *);
334 QPoint wOffset = qt_qwidget_data(widget)->wrect.topLeft();
335
336 if (widget->window() != window()) {
337 XFreeGC(X11->display, d_ptr->gc);
338 d_ptr->gc = XCreateGC(X11->display, widget->handle(), 0, 0);
339 }
340
341 QRegion wrgn(rgn);
342 if (!wOffset.isNull())
343 wrgn.translate(-wOffset);
344 QRect wbr = wrgn.boundingRect();
345
346 int num;
347 XRectangle *rects = (XRectangle *)qt_getClipRects(wrgn, num);
348 XSetClipRectangles(X11->display, d_ptr->gc, 0, 0, rects, num, YXBanded);
349
350 QRect br = rgn.boundingRect().translated(offset);
351#ifndef QT_NO_MITSHM
352 if (d_ptr->image->xshmpm) {
353 XCopyArea(X11->display, d_ptr->image->xshmpm, widget->handle(), d_ptr->gc,
354 br.x(), br.y(), br.width(), br.height(), wbr.x(), wbr.y());
355 XSync(X11->display, False);
356 } else
357#endif
358 {
359 const QImage &src = d->image->image;
360 br = br.intersected(src.rect());
361 if (src.format() != QImage::Format_RGB32) {
362 QX11PixmapData *data = new QX11PixmapData(QPixmapData::PixmapType);
363 data->xinfo = widget->x11Info();
364 data->fromImage(src, Qt::AutoColor);
365 QPixmap pm = QPixmap(data);
366 XCopyArea(X11->display, pm.handle(), widget->handle(), d_ptr->gc, br.x() , br.y() , br.width(), br.height(), wbr.x(), wbr.y());
367 } else {
368 // qpaintengine_x11.cpp
369 extern void qt_x11_drawImage(const QRect &rect, const QPoint &pos, const QImage &image, Drawable hd, GC gc, Display *dpy, Visual *visual, int depth);
370 qt_x11_drawImage(br, wbr.topLeft(), src, widget->handle(), d_ptr->gc, X11->display, (Visual *)widget->x11Info().visual(), widget->x11Info().depth());
371 }
372 }
373#endif // FALCON
374
375#ifdef Q_WS_MAC
376
377// qDebug() << "Flushing" << widget << rgn << offset;
378
379// d->image->image.save("flush.png");
380
381 // Get a context for the widget.
382#ifndef QT_MAC_USE_COCOA
383 CGContextRef context;
384 CGrafPtr port = GetWindowPort(qt_mac_window_for(widget));
385 QDBeginCGContext(port, &context);
386#else
387 extern CGContextRef qt_mac_graphicsContextFor(QWidget *);
388 CGContextRef context = qt_mac_graphicsContextFor(widget);
389#endif
390 CGContextSaveGState(context);
391
392 // Flip context.
393 CGContextTranslateCTM(context, 0, widget->height());
394 CGContextScaleCTM(context, 1, -1);
395
396 // Clip to region.
397 const QVector<QRect> &rects = rgn.rects();
398 for (int i = 0; i < rects.size(); ++i) {
399 const QRect &rect = rects.at(i);
400 CGContextAddRect(context, CGRectMake(rect.x(), rect.y(), rect.width(), rect.height()));
401 }
402 CGContextClip(context);
403
404 QRect r = rgn.boundingRect();
405 const CGRect area = CGRectMake(r.x(), r.y(), r.width(), r.height());
406 CGImageRef image = CGBitmapContextCreateImage(d->image->cg);
407 CGImageRef subImage = CGImageCreateWithImageInRect(image, area);
408
409 qt_mac_drawCGImage(context, &area, subImage);
410 CGImageRelease(subImage);
411 CGImageRelease(image);
412
413// CGSize size = { d->image->image.width(), d->image->image.height() };
414// CGLayerRef layer = CGLayerCreateWithContext(d->image->cg, size, 0);
415// CGPoint pt = { 0, 0 };
416// CGContextDrawLayerAtPoint(context, pt, layer);
417// CGLayerRelease(layer);
418
419 // Restore context.
420 CGContextRestoreGState(context);
421#ifndef QT_MAC_USE_COCOA
422 QDEndCGContext(port, &context);
423#else
424 CGContextFlush(context);
425#endif
426#endif
427}
428
429void QRasterWindowSurface::setGeometry(const QRect &rect)
430{
431 QWindowSurface::setGeometry(rect);
432 Q_D(QRasterWindowSurface);
433 d->inSetGeometry = true;
434 if (d->image == 0 || d->image->width() < rect.width() || d->image->height() < rect.height()) {
435#if (defined(Q_WS_X11) && !defined(QT_NO_XRENDER)) || (defined(Q_WS_WIN) && !defined(Q_OS_WINCE))
436#ifndef Q_WS_WIN
437 if (d_ptr->translucentBackground)
438#else
439 if (!qt_widget_private(window())->isOpaque && d->canUseLayeredWindow)
440#endif
441 prepareBuffer(QImage::Format_ARGB32_Premultiplied, window());
442 else
443#endif
444 prepareBuffer(QNativeImage::systemFormat(), window());
445 }
446 d->inSetGeometry = false;
447}
448
449// from qwindowsurface.cpp
450extern void qt_scrollRectInImage(QImage &img, const QRect &rect, const QPoint &offset);
451
452bool QRasterWindowSurface::scroll(const QRegion &area, int dx, int dy)
453{
454#ifdef Q_WS_WIN
455 Q_D(QRasterWindowSurface);
456
457 if (!d->image || !d->image->hdc)
458 return false;
459
460 QRect rect = area.boundingRect();
461 BitBlt(d->image->hdc, rect.x()+dx, rect.y()+dy, rect.width(), rect.height(),
462 d->image->hdc, rect.x(), rect.y(), SRCCOPY);
463
464 return true;
465#else
466 Q_D(QRasterWindowSurface);
467
468 if (!d->image || d->image->image.isNull())
469 return false;
470
471 const QVector<QRect> rects = area.rects();
472 for (int i = 0; i < rects.size(); ++i)
473 qt_scrollRectInImage(d->image->image, rects.at(i), QPoint(dx, dy));
474
475 return true;
476#endif
477}
478
479
480void QRasterWindowSurface::prepareBuffer(QImage::Format format, QWidget *widget)
481{
482 Q_D(QRasterWindowSurface);
483
484 int width = window()->width();
485 int height = window()->height();
486 if (d->image) {
487 width = qMax(d->image->width(), width);
488 height = qMax(d->image->height(), height);
489 }
490
491 if (width == 0 || height == 0) {
492 delete d->image;
493 d->image = 0;
494 return;
495 }
496
497 QNativeImage *oldImage = d->image;
498
499 d->image = new QNativeImage(width, height, format, false, widget);
500
501 if (oldImage && d->inSetGeometry && hasStaticContents()) {
502 // Make sure we use the const version of bits() (no detach).
503 const uchar *src = const_cast<const QImage &>(oldImage->image).bits();
504 uchar *dst = d->image->image.bits();
505
506 const int srcBytesPerLine = oldImage->image.bytesPerLine();
507 const int dstBytesPerLine = d->image->image.bytesPerLine();
508 const int bytesPerPixel = oldImage->image.depth() >> 3;
509
510 QRegion staticRegion(staticContents());
511 // Make sure we're inside the boundaries of the old image.
512 staticRegion &= QRect(0, 0, oldImage->image.width(), oldImage->image.height());
513 const QVector<QRect> &rects = staticRegion.rects();
514 const QRect *srcRect = rects.constData();
515
516 // Copy the static content of the old image into the new one.
517 int numRectsLeft = rects.size();
518 do {
519 const int bytesOffset = srcRect->x() * bytesPerPixel;
520 const int dy = srcRect->y();
521
522 // Adjust src and dst to point to the right offset.
523 const uchar *s = src + dy * srcBytesPerLine + bytesOffset;
524 uchar *d = dst + dy * dstBytesPerLine + bytesOffset;
525 const int numBytes = srcRect->width() * bytesPerPixel;
526
527 int numScanLinesLeft = srcRect->height();
528 do {
529 ::memcpy(d, s, numBytes);
530 d += dstBytesPerLine;
531 s += srcBytesPerLine;
532 } while (--numScanLinesLeft);
533
534 ++srcRect;
535 } while (--numRectsLeft);
536 }
537
538 delete oldImage;
539}
540
541QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.