From: sonyps5201314 sonyps5201314@gmail.com
--- dlls/winemac.drv/cocoa_window.m | 217 ++++++++++++++++++++++++++++++++ dlls/winemac.drv/gdi.c | 163 ++++++++++++++++++++++++ dlls/winemac.drv/macdrv_cocoa.h | 8 ++ 3 files changed, 388 insertions(+)
diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 2525c894d09..e5e0bc57027 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -3962,3 +3962,220 @@ void macdrv_clear_ime_text(void) [[window contentView] clearMarkedText]; }); } + +#define wxOSX_USE_PREMULTIPLIED_ALPHA 1 +static const int kBestByteAlignement = 16; +static const int kMaskBytesPerPixel = 1; + +static size_t GetBestBytesPerRow( size_t rawBytes ) +{ + return (((rawBytes)+kBestByteAlignement-1) & ~(kBestByteAlignement-1) ); +} + +static CGColorSpaceRef genericRGBColorSpace; +CGColorSpaceRef wxMacGetGenericRGBColorSpace() +{ + if (genericRGBColorSpace == NULL) + { +#if wxOSX_USE_IPHONE + genericRGBColorSpace = CGColorSpaceCreateDeviceRGB(); +#else + genericRGBColorSpace = CGColorSpaceCreateWithName( kCGColorSpaceSRGB ); +#endif + } + + return genericRGBColorSpace; +} + +OSStatus wxMacDrawCGImage( + CGContextRef inContext, + const CGRect * inBounds, + CGImageRef inImage) +{ + CGContextSaveGState(inContext); + CGContextTranslateCTM(inContext, inBounds->origin.x, inBounds->origin.y + inBounds->size.height); + CGRect r = *inBounds; + r.origin.x = r.origin.y = 0; + CGContextScaleCTM(inContext, 1, -1); + CGContextDrawImage(inContext, r, inImage ); + CGContextRestoreGState(inContext); + return noErr; +} + +CGContextRef Bitmap_Create(int w, int h, double contentScaleFactor, size_t* bitmap_width, size_t* bitmap_height) +{ + *bitmap_width = MAX(1, w*contentScaleFactor); + *bitmap_height = MAX(1, h*contentScaleFactor); + + size_t bytesPerRow = GetBestBytesPerRow(*bitmap_width * 4); + CGContextRef hBitmap = CGBitmapContextCreate(NULL, *bitmap_width, *bitmap_height, 8, bytesPerRow, wxMacGetGenericRGBColorSpace(), kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Little); + //wxCHECK_MSG(hBitmap, false, wxT("Unable to create CGBitmapContext context")); + if(hBitmap == NULL) + { + NSLog(@"Unable to create CGBitmapContext context"); + return NULL; + } + CGContextTranslateCTM(hBitmap, 0, *bitmap_height); + CGContextScaleCTM(hBitmap, 1 * contentScaleFactor, -1 * contentScaleFactor); + return hBitmap; +} + +void macdrv_get_image_from_screen(const struct wxRect *subrect, double contentScaleFactor, void* *pbits, int* pbytes_per_line) +{ + // Get the source rectangle + CGRect cgbounds; + cgbounds = CGDisplayBounds(CGMainDisplayID()); + int screen_width = cgbounds.size.width; + int screen_height = cgbounds.size.height; + struct wxRect fullRect = {0, 0, screen_width, screen_height}; + + struct wxRect rect; + BOOL needSubImage = FALSE; + if (subrect && memcmp(subrect, &fullRect, sizeof(struct wxRect)) != 0 ) + { + rect = *subrect; + needSubImage = TRUE; + } + else + { + rect = fullRect; + } + + // Create a bitmap in our format + //wxBitmap bmp(rect.GetSize(), 32); + size_t bitmap_width, bitmap_height; + CGContextRef hBitmap = Bitmap_Create(rect.width, rect.height, contentScaleFactor, &bitmap_width, &bitmap_height); + + // Capture full screen image + CGImageRef image = NULL; + + image = CGDisplayCreateImage(kCGDirectMainDisplay); + + if(image == NULL) + { + NSLog(@"wxScreenDC::GetAsBitmap - unable to get screenshot."); + return; + } + + if (needSubImage) + { + // Crop a sub image from a fullscreen image + CGImageRef fullImage = image; + image = CGImageCreateWithImageInRect(fullImage, CGRectMake(subrect->x, subrect->y, rect.width, rect.height)); + CGImageRelease(fullImage); + if(image == NULL) + { + NSLog(@"wxScreenDC::GetAsBitmap - unable to get screenshot."); + return; + } + } + + // Draw to a bitmap in our format + CGContextRef context = hBitmap; + + CGContextSaveGState(context); + + // Adjust the coordinate system of the bitmap in our format to be the same as that of Windows + CGContextTranslateCTM( context, 0, rect.height ); + CGContextScaleCTM( context, 1, -1 ); + + + // Set the quality level to use when rescaling +// CGContextSetAllowsAntialiasing(context, YES); +// CGContextSetShouldAntialias(context, YES); +// CGContextSetInterpolationQuality(context, kCGInterpolationHigh); + CGContextDrawImage(context, CGRectMake(0, 0, rect.width, rect.height), image); + + CGImageRelease(image); + + CGContextRestoreGState(context); + + // Export image for debugging + //CGImageRef ref = CGBitmapContextCreateImage(context); + + const unsigned char* sourcedata = (const unsigned char*)(CGBitmapContextGetData(hBitmap)); + int sourcelinesize = (int) CGBitmapContextGetBytesPerRow(hBitmap); + *pbytes_per_line = sourcelinesize; + void* pbuf = malloc(sourcelinesize*bitmap_height); + memcpy(pbuf, sourcedata, sourcelinesize*bitmap_height); + *pbits = pbuf; + + CGContextRelease(hBitmap); + hBitmap = NULL; +} + +void macdrv_get_image(macdrv_view v, const struct wxRect *subrect, double contentScaleFactor, void* *pbits, int* pbytes_per_line) +{ + WineContentView* view = (WineContentView*)v; + + //const wxSize bitmapSize(subrect ? subrect->GetSize() : m_window->GetSize()); + struct wxSize bitmapSize; + if (subrect) { + struct wxSize subSize = {subrect->width, subrect->height}; + bitmapSize = subSize; + } + else + { + struct wxSize fullSize = {view.bounds.size.width, view.bounds.size.height}; + bitmapSize = fullSize; + } + + // create bitmap + //wxBitmap bitmap; + //bitmap.CreateWithDIPSize(bitmapSize, contentScaleFactor); + size_t bitmap_width, bitmap_height; + CGContextRef hBitmap = Bitmap_Create(bitmapSize.x, bitmapSize.y, contentScaleFactor, &bitmap_width, &bitmap_height); + + // drawing + if ( [view isHiddenOrHasHiddenAncestor] == NO ) + { + // the old implementaiton is not working under 10.15, the new one should work for older systems as well + // however the new implementation does not take into account the backgroundViews, and I'm not sure about + // until we're + // sure the replacement is always better + + bool useOldImplementation = false; + NSBitmapImageRep *rep = nil; + + if ( useOldImplementation ) + { + [view lockFocus]; + // we use this method as other methods force a repaint, and this method can be + // called from OnPaint, even with the window's paint dc as source (see wxHTMLWindow) + rep = [[NSBitmapImageRep alloc] initWithFocusedViewRect: [view bounds]]; + [view unlockFocus]; + + } + else + { + rep = [view bitmapImageRepForCachingDisplayInRect:[view bounds]]; + [view cacheDisplayInRect:[view bounds] toBitmapImageRep:rep]; + } + + CGImageRef cgImageRef = (CGImageRef)[rep CGImage]; + + CGRect r = CGRectMake( 0 , 0 , CGImageGetWidth(cgImageRef) , CGImageGetHeight(cgImageRef) ); + + // The bitmap created by wxBitmap::CreateWithDIPSize() above is scaled, + // so we need to adjust the coordinates for it. + r.size.width /= contentScaleFactor; + r.size.height /= contentScaleFactor; + + // since our context is upside down we dont use CGContextDrawImage + wxMacDrawCGImage( hBitmap , &r, cgImageRef ) ; + + if ( useOldImplementation ) + [rep release]; + } + + const unsigned char* sourcedata = (const unsigned char*)(CGBitmapContextGetData(hBitmap)); + int sourcelinesize = (int) CGBitmapContextGetBytesPerRow(hBitmap); + *pbytes_per_line = sourcelinesize; + void* pbuf = malloc(sourcelinesize*bitmap_height); + memcpy(pbuf, sourcedata, sourcelinesize*bitmap_height); + *pbits = pbuf; + + CGContextRelease(hBitmap); + hBitmap = NULL; +} + diff --git a/dlls/winemac.drv/gdi.c b/dlls/winemac.drv/gdi.c index fd1da722061..5b7a6d32452 100644 --- a/dlls/winemac.drv/gdi.c +++ b/dlls/winemac.drv/gdi.c @@ -215,6 +215,168 @@ static BOOL CDECL macdrv_DeleteDC(PHYSDEV dev) return TRUE; }
+static void CDECL free_heap_bits( struct gdi_image_bits *bits ) +{ + free( bits->ptr ); +} + +typedef unsigned long VisualID; +typedef struct { + void *visual; + VisualID visualid; + int screen; + int depth; +#if defined(__cplusplus) || defined(c_plusplus) + int c_class; /* C++ */ +#else + int class; +#endif + unsigned long red_mask; + unsigned long green_mask; + unsigned long blue_mask; + int colormap_size; + int bits_per_rgb; +} XVisualInfo; + +/* Maps pixel to the entry in the system palette */ +int *X11DRV_PALETTE_XPixelToPalette = NULL; + +/* store the palette or color mask data in the bitmap info structure */ +static void set_color_info( const XVisualInfo *vis, BITMAPINFO *info, BOOL has_alpha ) +{ + DWORD *colors = (DWORD *)((char *)info + info->bmiHeader.biSize); + + info->bmiHeader.biCompression = BI_RGB; + info->bmiHeader.biClrUsed = 0; + + switch (info->bmiHeader.biBitCount) + { + case 4: + case 8: + { + RGBQUAD *rgb = (RGBQUAD *)colors; + PALETTEENTRY palette[256]; + UINT i, count; + + info->bmiHeader.biClrUsed = 1 << info->bmiHeader.biBitCount; + //count = X11DRV_GetSystemPaletteEntries( NULL, 0, info->bmiHeader.biClrUsed, palette ); + count = 0; + for (i = 0; i < count; i++) + { + rgb[i].rgbRed = palette[i].peRed; + rgb[i].rgbGreen = palette[i].peGreen; + rgb[i].rgbBlue = palette[i].peBlue; + rgb[i].rgbReserved = 0; + } + memset( &rgb[count], 0, (info->bmiHeader.biClrUsed - count) * sizeof(*rgb) ); + break; + } + case 16: + colors[0] = vis->red_mask; + colors[1] = vis->green_mask; + colors[2] = vis->blue_mask; + info->bmiHeader.biCompression = BI_BITFIELDS; + break; + case 32: + colors[0] = vis->red_mask; + colors[1] = vis->green_mask; + colors[2] = vis->blue_mask; + if (colors[0] != 0xff0000 || colors[1] != 0x00ff00 || colors[2] != 0x0000ff || !has_alpha) + info->bmiHeader.biCompression = BI_BITFIELDS; + break; + } +} + +extern macdrv_view macdrv_get_cocoa_view(HWND hwnd); + +void macdrv_get_image_from_screen(const struct wxRect *subrect, double contentScaleFactor, void* *pbits, int* pbytes_per_line); +void macdrv_get_image(macdrv_view v, const struct wxRect *subrect, double contentScaleFactor, void* *pbits, int* pbytes_per_line); + +/*********************************************************************** + * macdrv_GetImage + */ +DWORD CDECL macdrv_GetImage( PHYSDEV dev, BITMAPINFO *info, + struct gdi_image_bits *bits, struct bitblt_coords *src ) +{ + MACDRV_PDEVICE *physdev = get_macdrv_dev(dev); + DWORD ret = ERROR_SUCCESS; + XVisualInfo vis = {0}; + UINT align, x, y, width, height; + const int *mapping = NULL; + + vis.depth = bits_per_pixel; + + vis.red_mask = 0xff0000; + vis.green_mask = 0x00ff00; + vis.blue_mask = 0x0000ff; + + /* align start and width to 32-bit boundary */ + switch (bits_per_pixel) + { + case 1: align = 32; break; + case 4: align = 8; mapping = X11DRV_PALETTE_XPixelToPalette; break; + case 8: align = 4; mapping = X11DRV_PALETTE_XPixelToPalette; break; + case 16: align = 2; break; + case 24: align = 4; break; + case 32: align = 1; break; + default: + FIXME( "depth %u bpp %u not supported yet\n", vis.depth, bits_per_pixel ); + return ERROR_BAD_FORMAT; + } + + info->bmiHeader.biSize = sizeof(info->bmiHeader); + info->bmiHeader.biPlanes = 1; + info->bmiHeader.biBitCount = bits_per_pixel; + info->bmiHeader.biXPelsPerMeter = 0; + info->bmiHeader.biYPelsPerMeter = 0; + info->bmiHeader.biClrImportant = 0; + set_color_info( &vis, info, FALSE ); + + if (!bits) return ERROR_SUCCESS; /* just querying the color information */ + + x = src->visrect.left & ~(align - 1); + y = src->visrect.top; + width = src->visrect.right - x; + height = src->visrect.bottom - src->visrect.top; + //if (format->scanline_pad != 32) width = (width + (align - 1)) & ~(align - 1); + /* make the source rectangle relative to the returned bits */ + src->x -= x; + src->y -= y; + OffsetRect( &src->visrect, -x, -y ); + + //Get image from the platform device + int bytes_per_line = 0; + HWND hwnd = NtUserWindowFromDC(dev->hdc); + // We will only create 32 bit bitmaps + if (hwnd == NULL || hwnd == NtUserGetDesktopWindow()) + { + struct wxRect subrect = {src->log_x, src->log_y, src->log_width, src->log_height}; + macdrv_get_image_from_screen(&subrect, 1.0, &bits->ptr, &bytes_per_line); + } + else + { + macdrv_view view = macdrv_get_cocoa_view(hwnd); + if (view != NULL) + { + struct wxRect subrect = {src->log_x, src->log_y, src->log_width, src->log_height}; + macdrv_get_image(view, &subrect, 1.0, &bits->ptr, &bytes_per_line); + } + else + { + // Window in other process are not currently supported + FIXME( "Window in other process is not supported yet\n"); + } + } + if (bits->ptr) { + bits->is_copy = TRUE; + bits->free = free_heap_bits; + } + + info->bmiHeader.biWidth = width; + info->bmiHeader.biHeight = -height; + info->bmiHeader.biSizeImage = height * bytes_per_line; + return ret; +}
/*********************************************************************** * GetDeviceCaps (MACDRV.@) @@ -263,6 +425,7 @@ static const struct user_driver_funcs macdrv_funcs = .dc_funcs.pDeleteDC = macdrv_DeleteDC, .dc_funcs.pGetDeviceCaps = macdrv_GetDeviceCaps, .dc_funcs.pGetDeviceGammaRamp = macdrv_GetDeviceGammaRamp, + .dc_funcs.pGetImage = macdrv_GetImage, .dc_funcs.pSetDeviceGammaRamp = macdrv_SetDeviceGammaRamp, .dc_funcs.priority = GDI_PRIORITY_GRAPHICS_DRV,
diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 6196032c08d..618ebcdda0c 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -150,6 +150,14 @@ struct macdrv_display { CGRect work_frame; };
+//wxWidgets +struct wxSize { + int x, y; +}; +struct wxRect { + int x, y, width, height; +}; +
/* main */ extern int macdrv_err_on;
Unfortunately dropping a big chunk of code from a different library, that also uses a different license, is not acceptable.
Afaics the main line here is
image = CGDisplayCreateImage(kCGDirectMainDisplay);
The rest of the code brings the returned data into the right format and crops a subrectangle, if necessary. Wine has code for this already (in form of Win32 / NtGdi functions), there's no need to copypaste it from WX. It is also possible that the upper layer (win32u, user32) already handle part of this task.
On the MacOS platform, the BitBlt and StretchBlt APIs are invalid whether copying bitmap data from the desktop window or other windows of the process, because the underlying GetImage function of gdi in wine is not implemented on the MacOS platform, and in wine I did not find similar reference codes that can be reused for modification. I only found some relevant codes that can be reused in the gdi implementation of the x11 driver of wine, and have already used them. My code is not completely consistent with wxwidget Yes, the implementation of wxwidget also has bugs. I have modified and corrected it a lot. The function name is not changed as a tribute to the original code. Of course, if you think it is necessary, I can also rename it to avoid potential license issues. , I own the copyright to it, just for general reference.
The core function of GetImage is to convert the specific bitmap format of the Unix operating system platform into the dib device-independent bitmap format of the Windows specification, and those functions in the gdi null driver of wine are only converted between dib bitmap data, so it does not To meet this requirement, the only reference is the x11 driver of wine, and I have already used it. Or you can tell me which codes in wine can be used to complete this task, but you need to pay attention to the bitmap on MacOS, the coordinate system is different from that used by gdi, and conversion operations such as flipping, translation and zooming are required. These codes have been tested and used in my projects for a long time.
And it is not convenient and simple to call the gdi function in the m file. I didn't even find gdi32, user32 or ntgdi, ntuser related calls in cocoa_window.m, and that would increase coupling, reduce independence and increase complexity, and Not good for maintenance.