screenshot icon indicating copy to clipboard operation
screenshot copied to clipboard

Screenshot is merged when multiple display screens are connected

Open kashishm opened this issue 8 years ago • 8 comments

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

s

kashishm avatar Jan 11 '17 06:01 kashishm

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
}

vova616 avatar Jan 11 '17 07:01 vova616

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

kashishm avatar Jan 11 '17 08:01 kashishm

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?

kashishm avatar Jan 11 '17 08:01 kashishm

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

vova616 avatar Jan 11 '17 09:01 vova616

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 ;)

kesarion avatar Feb 01 '17 13:02 kesarion

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.

ivoszz avatar Mar 09 '17 17:03 ivoszz

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

pftbest avatar Mar 10 '17 08:03 pftbest

@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, ...).

ivoszz avatar Mar 13 '17 09:03 ivoszz