Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Screenshot is merged when multiple display screens are connected #9

Open
kashishm opened this issue Jan 11, 2017 · 8 comments
Open

Comments

@kashishm
Copy link

It is working fine with one display screen but gives a merged screenshot of multiple displays on mac.

s

@vova616
Copy link
Owner

vova616 commented Jan 11, 2017

can you try this one and tell me how it works?

package screenshot

import (
	// #cgo LDFLAGS: -framework CoreGraphics
	// #cgo LDFLAGS: -framework CoreFoundation
	// #include <CoreGraphics/CoreGraphics.h>
	// #include <CoreFoundation/CoreFoundation.h>
	"C"
	"image"
	"reflect"
	"unsafe"
)

func ScreenRect() (image.Rectangle, error) {
	displayID := C.CGMainDisplayID()
	width := int(C.CGDisplayPixelsWide(displayID))
	height := int(C.CGDisplayPixelsHigh(displayID))
	return image.Rect(0, 0, width, height), nil
}

func CaptureScreen() (*image.RGBA, error) {
	rect, err := ScreenRect()
	if err != nil {
		return nil, err
	}
	return CaptureRect(rect)
}

func CaptureRect(rect image.Rectangle) (*image.RGBA, error) {
	displayID := C.CGMainDisplayID()

	list := make([]C.CGDirectDisplayID, 16)
	size := uint32(0)
	C.CGGetOnlineDisplayList(C.uint32_t(cap(list)), (*C.CGDirectDisplayID)(unsafe.Pointer(&list[0])), (*C.uint32_t)(&size))

	if size > 0 {
		displayID = list[0]
	}

	width := int(C.CGDisplayPixelsWide(displayID))
	rawData := C.CGDataProviderCopyData(C.CGImageGetDataProvider(C.CGDisplayCreateImage(displayID)))

	length := int(C.CFDataGetLength(rawData))
	ptr := unsafe.Pointer(C.CFDataGetBytePtr(rawData))

	var slice []byte
	hdrp := (*reflect.SliceHeader)(unsafe.Pointer(&slice))
	hdrp.Data = uintptr(ptr)
	hdrp.Len = length
	hdrp.Cap = length

	imageBytes := make([]byte, length)

	for i := 0; i < length; i += 4 {
		imageBytes[i], imageBytes[i+2], imageBytes[i+1], imageBytes[i+3] = slice[i+2], slice[i], slice[i+1], slice[i+3]
	}

	C.CFRelease(rawData)

	img := &image.RGBA{Pix: imageBytes, Stride: 4 * width, Rect: rect}
	return img, nil
}

@kashishm
Copy link
Author

Thanks for the quick reply. With the above code also, the screenshot is same.

@kashishm
Copy link
Author

We have forked this repo and we were using screencapture for taking screenshots in darwin. It is working fine and can take screenshots for multiple displays as well. Here's the code.
Is there any specific reason for not using it?

@vova616
Copy link
Owner

vova616 commented Jan 11, 2017

Thanks for checking :).
I guess because his code is a using a different binary to take the screenshot, I will try to find how to fix it during the week

@kesarion
Copy link
Contributor

kesarion commented Feb 1, 2017

The CG framework does have support for multiple displays, but I didn't go so far when I implemented the darwin version (it was simpler to just get the main display). The advantage of using CG over screencapture is you don't need to write to a file and thus you have no issues with file permissions. Also, the library has better chances of outliving the tool.

I can work out a multi display version when I get the time ;)

@ivoszz
Copy link

ivoszz commented Mar 9, 2017

The problem is not (probably) caused by multiple displays. Today I played with your library and received the same result. Looking deeper into the function CaptureRect() I discovered that display resolution returns screen size (e.g. 1440x900), but returned Image is 2880x1800 on my retina display. This caused corruption of the image. I was able to get correct picture tweaking your function, but 2x bigger. Because I don't know Apple Core framework I wasn't able to scale down the picture to desired resolution.
Hope this help.

@pftbest
Copy link
Contributor

pftbest commented Mar 10, 2017

15' Retina display has in fact 2880x1800 pixels. So the image is probably correct, and 1440x900 number is wrong.

@ivoszz
Copy link

ivoszz commented Mar 13, 2017

@pftbest: I don't use native resolution on my 13" notebook, I use 1440x900. So both numbers are correct. Screen resolution is 1440x900 (reported by CGDisplayPixelsWide), but screenshot has native display resolution 2560x1600 (reported by CGDisplayCreateImage - sorry, I mentioned wrong numbers in my original comment).
The image is than iterated using width reported by CGDisplayPixelsWide with real screen resolution in the function CaptureRect and this iteration distorts the image. In the original comment from @kashishm the issue is probably that plugging another monitor changes the screen resolution.
Solution should be to rescale the image returned by CGDisplayPixelsWide to real screen resolution if they are different. When I work with my screen I want to use screen points, not native display resolution.
This problem can also affect other implementations (Linux, Windows, ...).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants