VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/runtime/UIFrameBufferQuartz2D.cpp@ 35740

Last change on this file since 35740 was 35615, checked in by vboxsync, 13 years ago

FE/Qt4-OSX: follow up to r69510

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 17.2 KB
Line 
1/* $Id: UIFrameBufferQuartz2D.cpp 35615 2011-01-18 14:48:41Z vboxsync $ */
2/** @file
3 *
4 * VBox frontends: Qt GUI ("VirtualBox"):
5 * UIFrameBufferQuartz2D class implementation
6 */
7
8/*
9 * Copyright (C) 2006-2010 Oracle Corporation
10 *
11 * This file is part of VirtualBox Open Source Edition (OSE), as
12 * available from http://www.virtualbox.org. This file is free software;
13 * you can redistribute it and/or modify it under the terms of the GNU
14 * General Public License (GPL) as published by the Free Software
15 * Foundation, in version 2 as it comes in the "COPYING" file of the
16 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
17 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
18 */
19
20#if defined (VBOX_GUI_USE_QUARTZ2D)
21
22#ifdef VBOX_WITH_PRECOMPILED_HEADERS
23# include "precomp.h"
24#else /* !VBOX_WITH_PRECOMPILED_HEADERS */
25/* Global includes */
26#include <QApplication>
27
28#include <iprt/asm.h>
29
30/* Local includes */
31#include "UIFrameBufferQuartz2D.h"
32#include "UIMachineView.h"
33#include "UIMachineLogic.h"
34#include "VBoxUtils.h"
35
36#endif /* !VBOX_WITH_PRECOMPILED_HEADERS */
37
38//#define COMP_WITH_SHADOW
39//#define OVERLAY_CLIPRECTS
40
41/** @class UIFrameBufferQuartz2D
42 *
43 * The UIFrameBufferQuartz2D class is a class that implements the IFrameBuffer
44 * interface and uses Apples Quartz2D to store and render VM display data.
45 */
46
47UIFrameBufferQuartz2D::UIFrameBufferQuartz2D(UIMachineView *pMachineView)
48 : UIFrameBuffer(pMachineView)
49 , m_pMachineLogic(pMachineView->machineLogic())
50 , m_pDataAddress(NULL)
51 , m_pBitmapData(NULL)
52 , m_uPixelFormat(FramebufferPixelFormat_FOURCC_RGB)
53 , m_image(NULL)
54 , mRegion(NULL)
55 , mRegionUnused(NULL)
56{
57 Log (("Quartz2D: Creating\n"));
58
59 UIResizeEvent event(FramebufferPixelFormat_Opaque,
60 NULL, 0, 0, 640, 480);
61 resizeEvent (&event);
62}
63
64UIFrameBufferQuartz2D::~UIFrameBufferQuartz2D()
65{
66 Log (("Quartz2D: Deleting\n"));
67 clean();
68}
69
70/** @note This method is called on EMT from under this object's lock */
71STDMETHODIMP UIFrameBufferQuartz2D::NotifyUpdate(ULONG aX, ULONG aY,
72 ULONG aW, ULONG aH)
73{
74/* Log (("Quartz2D: NotifyUpdate %d,%d %dx%d\n", aX, aY, aW, aH));*/
75
76 QApplication::postEvent(m_pMachineView,
77 new UIRepaintEvent (aX, aY, aW, aH));
78 return S_OK;
79}
80
81STDMETHODIMP UIFrameBufferQuartz2D::SetVisibleRegion(BYTE *aRectangles, ULONG aCount)
82{
83 PRTRECT rects = (PRTRECT)aRectangles;
84
85 if (!rects)
86 return E_POINTER;
87
88 /** @todo r=bird: Is this thread safe? If I remember the code flow correctly, the
89 * GUI thread could be happily jogging along paintEvent now on another cpu core.
90 * This function is called on the EMT (emulation thread). Which means, blocking
91 * execution waiting for a lock is out of the question. A quick solution using
92 * ASMAtomic(Cmp)XchgPtr and a struct { cAllocated; cRects; aRects[1]; }
93 * *mRegion, *mUnusedRegion; should suffice (and permit you to reuse allocations). */
94 RegionRects *rgnRcts = ASMAtomicXchgPtrT(&mRegionUnused, NULL, RegionRects *);
95 if (rgnRcts && rgnRcts->allocated < aCount)
96 {
97 RTMemFree (rgnRcts);
98 rgnRcts = NULL;
99 }
100 if (!rgnRcts)
101 {
102 ULONG allocated = RT_ALIGN_32(aCount + 1, 32);
103 allocated = RT_MAX (128, allocated);
104 rgnRcts = (RegionRects *)RTMemAlloc(RT_OFFSETOF(RegionRects, rcts[allocated]));
105 if (!rgnRcts)
106 return E_OUTOFMEMORY;
107 rgnRcts->allocated = allocated;
108 }
109 rgnRcts->used = 0;
110
111 QRegion reg;
112// printf ("Region rects follow...\n");
113 QRect vmScreenRect (0, 0, width(), height());
114 for (ULONG ind = 0; ind < aCount; ++ ind)
115 {
116 QRect rect;
117 rect.setLeft(rects->xLeft);
118 rect.setTop(rects->yTop);
119 /* QRect are inclusive */
120 rect.setRight(rects->xRight - 1);
121 rect.setBottom(rects->yBottom - 1);
122
123 /* The rect should intersect with the vm screen. */
124 rect = vmScreenRect.intersect(rect);
125 ++ rects;
126 /* Make sure only valid rects are distributed */
127 /* todo: Test if the other framebuffer implementation have the same
128 * problem with invalid rects (In Linux/Windows) */
129 if (rect.isValid() &&
130 rect.width() > 0 && rect.height() > 0)
131 reg += rect;
132 else
133 continue;
134
135 CGRect *cgRct = &rgnRcts->rcts[rgnRcts->used];
136 cgRct->origin.x = rect.x();
137 cgRct->origin.y = height() - rect.y() - rect.height();
138 cgRct->size.width = rect.width();
139 cgRct->size.height = rect.height();
140// printf ("Region rect[%d - %d]: %d %d %d %d\n", rgnRcts->used, aCount, rect.x(), rect.y(), rect.height(), rect.width());
141 rgnRcts->used++;
142 }
143// printf ("..................................\n");
144
145 RegionRects *pOld = ASMAtomicXchgPtrT(&mRegion, rgnRcts, RegionRects *);
146 if ( pOld
147 && !ASMAtomicCmpXchgPtr(&mRegionUnused, pOld, NULL))
148 RTMemFree(pOld);
149
150 QApplication::postEvent(m_pMachineView, new UISetRegionEvent (reg));
151
152 return S_OK;
153}
154
155void UIFrameBufferQuartz2D::paintEvent(QPaintEvent *aEvent)
156{
157 /* For debugging /Developer/Applications/Performance Tools/Quartz
158 * Debug.app is a nice tool to see which parts of the screen are
159 * updated.*/
160 Assert(m_image);
161
162 QWidget* viewport = m_pMachineView->viewport();
163 Assert(VALID_PTR(viewport));
164 /* Get the dimensions of the viewport */
165 CGRect viewRect = ::darwinToCGRect(viewport->geometry());
166 /* Get the context of this window from Qt */
167 CGContextRef ctx = ::darwinToCGContextRef(viewport);
168 Assert(VALID_PTR(ctx));
169
170 /* Flip the context */
171 CGContextTranslateCTM(ctx, 0, viewRect.size.height);
172 CGContextScaleCTM(ctx, 1.0, -1.0);
173
174 /* We handle the seamless mode as a special case. */
175 if (m_pMachineLogic->visualStateType() == UIVisualStateType_Seamless)
176 {
177 /* Here we paint the windows without any wallpaper.
178 * So the background would be set transparently. */
179
180 /* Create a subimage of the current view.
181 * Currently this subimage is the whole screen. */
182 CGImageRef subImage;
183 if (!m_pMachineView->pauseShot().isNull())
184 {
185 CGImageRef pauseImg = ::darwinToCGImageRef(&m_pMachineView->pauseShot());
186 subImage = CGImageCreateWithImageInRect(pauseImg, CGRectMake(m_pMachineView->contentsX(), m_pMachineView->contentsY(), m_pMachineView->visibleWidth(), m_pMachineView->visibleHeight()));
187 CGImageRelease(pauseImg);
188 }
189 else
190 subImage = CGImageCreateWithImageInRect(m_image, CGRectMake(m_pMachineView->contentsX(), m_pMachineView->contentsY(), m_pMachineView->visibleWidth(), m_pMachineView->visibleHeight()));
191 Assert(VALID_PTR(subImage));
192 /* Clear the background (Make the rect fully transparent) */
193 CGContextClearRect(ctx, viewRect);
194#ifdef OVERLAY_CLIPRECTS
195 CGContextSetRGBFillColor(ctx, 0.0, 0.0, 5.0, 0.7);
196 CGContextFillRect(ctx, viewRect);
197#endif
198#ifdef COMP_WITH_SHADOW
199 /* Enable shadows */
200 CGContextSetShadow(ctx, CGSizeMake (10, -10), 10);
201 CGContextBeginTransparencyLayer(ctx, NULL);
202#endif
203 /* Grab the current visible region. */
204 RegionRects *rgnRcts = ASMAtomicXchgPtrT(&mRegion, NULL, RegionRects *);
205 if (rgnRcts)
206 {
207 if (rgnRcts->used > 0)
208 {
209 /* Add the clipping rects all at once. They are defined in
210 * SetVisibleRegion. */
211 CGContextBeginPath(ctx);
212 CGContextAddRects(ctx, rgnRcts->rcts, rgnRcts->used);
213 /* Now convert the path to a clipping path. */
214 CGContextClip(ctx);
215 }
216 /* Put back the visible region, free if we cannot (2+ SetVisibleRegion calls). */
217 if ( !ASMAtomicCmpXchgPtr(&mRegion, rgnRcts, NULL)
218 && !ASMAtomicCmpXchgPtr(&mRegionUnused, rgnRcts, NULL))
219 RTMemFree(rgnRcts);
220 }
221 /* In any case clip the drawing to the view window */
222 CGContextClipToRect(ctx, viewRect);
223 /* At this point draw the real vm image */
224 CGContextDrawImage(ctx, ::darwinFlipCGRect (viewRect, viewRect.size.height), subImage);
225#ifdef COMP_WITH_SHADOW
226 CGContextEndTransparencyLayer(ctx);
227#endif
228 CGImageRelease(subImage);
229#ifdef OVERLAY_CLIPRECTS
230 if (rgnRcts && rgnRcts->used > 0)
231 {
232 CGContextBeginPath(ctx);
233 CGContextAddRects(ctx, rgnRcts->rcts, rgnRcts->used);
234 CGContextSetRGBStrokeColor(ctx, 1.0, 0.0, 0.0, 0.7);
235 CGContextDrawPath(ctx, kCGPathStroke);
236 }
237 CGContextSetRGBStrokeColor(ctx, 0.0, 1.0, 0.0, 0.7);
238 CGContextStrokeRect(ctx, viewRect);
239#endif
240 }
241 else if ( m_pMachineLogic->visualStateType() == UIVisualStateType_Scale
242 && m_scaledSize.isValid())
243 {
244 /* Here we paint if we didn't care about any masks */
245
246 /* Create a subimage of the current view in the size
247 * of the bounding box of the current paint event */
248 CGRect ir = ::darwinToCGRect(aEvent->rect());
249 CGRect is = CGRectMake(CGRectGetMinX(ir), CGRectGetMinY(ir), CGRectGetWidth(ir), CGRectGetHeight(ir));
250
251 double iw = 1.0;
252 double ih = 1.0;
253 CGImageRef subImage = 0;
254 if (!m_pMachineView->pauseShot().isNull())
255 {
256 /* The pause image is already scaled. Maybe we should change that,
257 * to have a more uniform behavior and the different
258 * implementations use there own scaling algorithm. */
259 CGImageRef pauseImg = ::darwinToCGImageRef(&m_pMachineView->pauseShot());
260 subImage = CGImageCreateWithImageInRect(pauseImg, is);
261 CGImageRelease(pauseImg);
262 }
263 else
264 {
265 /* Scale all values needed for the image drawing. */
266 iw = (double)CGImageGetWidth(m_image) / m_scaledSize.width();
267 ih = (double)CGImageGetHeight(m_image) / m_scaledSize.height();
268 /* We make sure the image is always bigger than requested to
269 * compensate rounding errors. */
270 /* Round down */
271 is.origin.x = (int)(is.origin.x * iw);
272 is.origin.y = (int)(is.origin.y * ih);
273 /* Round up */
274 is.size.width = (int)(is.size.width * iw) + 2;
275 is.size.height = (int)(is.size.height * ih) + 2;
276 /* Make sure the size is within the image boundaries */
277 CGRect is1 = CGRectIntersection(is, CGRectMake(0, 0, CGImageGetWidth(m_image), CGImageGetHeight(m_image)));
278 /* Cause we probably changed the rectangle to update in the origin
279 * coordinate system, we have to recalculate the update rectangle
280 * for the screen coordinates as well. Please note that this has to
281 * be in double precision. */
282 ir.origin.x = is1.origin.x / iw;
283 ir.origin.y = is1.origin.y / ih;
284 ir.size.width = is1.size.width / iw;
285 ir.size.height = is1.size.height / ih;
286 /* Create the sub image. Note: The copy step is necessary otherwise
287 * strange things happens especially on Snow Leopard. No idea why. */
288 CGImageRef tmpImage = CGImageCreateWithImageInRect(m_image, is1);
289 subImage = CGImageCreateCopy(tmpImage);
290 CGImageRelease(tmpImage);
291 }
292 if (subImage)
293 {
294 /* For more performance we set a clipping path of the regions given
295 * by this paint event. */
296 QVector<QRect> a = aEvent->region().rects();
297 if (!a.isEmpty())
298 {
299 CGContextBeginPath(ctx);
300 /* Add all region rects to the current context as path components */
301 for (int i = 0; i < a.size(); ++i)
302 CGContextAddRect(ctx, ::darwinFlipCGRect(::darwinToCGRect(a.at(i)), CGRectGetHeight(viewRect)));
303 /* Now convert the path to a clipping path. */
304 CGContextClip(ctx);
305 }
306 /* In any case clip the drawing to the view window */
307 CGContextClipToRect(ctx, viewRect);
308 /* Turn the high interpolation quality on. */
309 CGContextSetInterpolationQuality(ctx, kCGInterpolationHigh);
310 /* Draw the image. */
311 CGContextDrawImage(ctx, ::darwinFlipCGRect(ir, CGRectGetHeight(viewRect)), subImage);
312 CGImageRelease(subImage);
313 }
314 }
315 else
316 {
317 /* Here we paint if we didn't care about any masks */
318
319 /* Create a subimage of the current view in the size
320 * of the bounding box of the current paint event */
321 QRect ir = aEvent->rect();
322 QRect is = QRect(ir.x() + m_pMachineView->contentsX(), ir.y() + m_pMachineView->contentsY(), ir.width(), ir.height());
323 CGImageRef subImage = 0;
324 if (!m_pMachineView->pauseShot().isNull())
325 {
326 CGImageRef pauseImg = ::darwinToCGImageRef(&m_pMachineView->pauseShot());
327 subImage = CGImageCreateWithImageInRect(pauseImg, ::darwinToCGRect(is));
328 CGImageRelease(pauseImg);
329 }
330 else
331 subImage = CGImageCreateWithImageInRect(m_image, ::darwinToCGRect(is));
332 if (subImage)
333 {
334 /* Ok, for more performance we set a clipping path of the
335 * regions given by this paint event. */
336 QVector<QRect> a = aEvent->region().rects();
337 if (!a.isEmpty())
338 {
339 CGContextBeginPath (ctx);
340 /* Add all region rects to the current context as path components */
341 for (int i = 0; i < a.size(); ++i)
342 CGContextAddRect(ctx, ::darwinFlipCGRect(::darwinToCGRect(a[i]), viewRect.size.height));
343 /* Now convert the path to a clipping path. */
344 CGContextClip(ctx);
345 }
346
347 /* In any case clip the drawing to the view window */
348 CGContextClipToRect(ctx, viewRect);
349
350 /* At this point draw the real vm image */
351 CGContextDrawImage(ctx, ::darwinFlipCGRect(::darwinToCGRect(ir), viewRect.size.height), subImage);
352 CGImageRelease(subImage);
353 }
354 }
355}
356
357void UIFrameBufferQuartz2D::resizeEvent(UIResizeEvent *aEvent)
358{
359#if 0
360 printf ("fmt=%lu, vram=%X, bpp=%lu, bpl=%lu, width=%lu, height=%lu\n",
361 aEvent->pixelFormat(), (unsigned int)aEvent->VRAM(),
362 aEvent->bitsPerPixel(), aEvent->bytesPerLine(),
363 aEvent->width(), aEvent->height());
364#endif
365
366 /* Clean out old stuff */
367 clean();
368
369 m_width = aEvent->width();
370 m_height = aEvent->height();
371
372 bool remind = false;
373
374 /* We need a color space in any case */
375 CGColorSpaceRef cs = CGColorSpaceCreateDeviceRGB();
376 /* Check if we support the pixel format/colordepth and can use the guest VRAM directly.
377 * Mac OS X supports 16 bit also but not in the 565 mode. So we could use
378 * 32 bit only. */
379 if ( aEvent->pixelFormat() == FramebufferPixelFormat_FOURCC_RGB
380 && aEvent->bitsPerPixel() == 32)
381 {
382// printf ("VRAM\n");
383 /* Create the image copy of the framebuffer */
384 CGDataProviderRef dp = CGDataProviderCreateWithData(NULL, aEvent->VRAM(), aEvent->bytesPerLine() * m_height, NULL);
385 m_image = CGImageCreate(m_width, m_height, 8, aEvent->bitsPerPixel(), aEvent->bytesPerLine(), cs,
386 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, dp, 0, false,
387 kCGRenderingIntentDefault);
388 m_pDataAddress = aEvent->VRAM();
389 CGDataProviderRelease (dp);
390 }
391 else
392 {
393 remind = true;
394// printf ("No VRAM\n");
395 /* Create the memory we need for our image copy
396 * Read somewhere that an alignment of 16 is
397 * best for optimal performance. So why not. */
398// int bitmapBytesPerRow = RT_ALIGN (m_width * 4, 16);
399 int bitmapBytesPerRow = m_width * 4;
400 int bitmapByteCount = (bitmapBytesPerRow * m_height);
401 m_pBitmapData = RTMemAllocZ(bitmapByteCount);
402 CGDataProviderRef dp = CGDataProviderCreateWithData(NULL, m_pBitmapData, bitmapByteCount, NULL);
403 m_image = CGImageCreate(m_width, m_height, 8, 32, bitmapBytesPerRow, cs,
404 kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little, dp, 0, false,
405 kCGRenderingIntentDefault);
406 m_pDataAddress = static_cast<uchar*>(m_pBitmapData);
407 CGDataProviderRelease(dp);
408 }
409 CGColorSpaceRelease(cs);
410#ifdef VBOX_WITH_ICHAT_THEATER
411 setImageRef(m_image);
412#endif
413
414// if (remind)
415// vboxProblem().remindAboutWrongColorDepth(aEvent->bitsPerPixel(), 32);
416}
417
418void UIFrameBufferQuartz2D::clean()
419{
420 if (m_image)
421 {
422 CGImageRelease(m_image);
423 m_image = NULL;
424 }
425 if (m_pBitmapData)
426 {
427 RTMemFree(m_pBitmapData);
428 m_pBitmapData = NULL;
429 }
430 if (mRegion)
431 {
432 RTMemFree((void *)mRegion);
433 mRegion = NULL;
434 }
435 if (mRegionUnused)
436 {
437 RTMemFree((void *)mRegionUnused);
438 mRegionUnused = NULL;
439 }
440}
441
442#ifdef VBOX_WITH_VIDEOHWACCEL
443void UIFrameBufferQuartz2D::setView(UIMachineView *pView)
444{
445 if (pView)
446 m_pMachineLogic = pView->machineLogic();
447
448 UIFrameBuffer::setView(pView);
449}
450#endif
451
452#endif /* defined (VBOX_GUI_USE_QUARTZ2D) */
453
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use