44Source: https://github.com/BoboTiG/python-mss
55"""
66
7- # pylint: disable=import-error
7+ # pylint: disable=import-error, too-many-locals
88
99from __future__ import division
1010
1111import ctypes
1212import ctypes .util
13- import math
1413import sys
1514
1615from .base import MSSBase
1716from .exception import ScreenShotError
17+ from .screenshot import Size
1818
19- __all__ = [ 'MSS' ]
19+ __all__ = ( 'MSS' ,)
2020
2121
2222def cgfloat ():
@@ -94,10 +94,13 @@ def _set_argtypes(self):
9494 ctypes .c_uint32 ,
9595 ctypes .c_uint32 ]
9696 self .core .CGImageGetWidth .argtypes = [ctypes .c_void_p ]
97+ self .core .CGImageGetHeight .argtypes = [ctypes .c_void_p ]
9798 self .core .CGImageGetDataProvider .argtypes = [ctypes .c_void_p ]
9899 self .core .CGDataProviderCopyData .argtypes = [ctypes .c_void_p ]
99100 self .core .CFDataGetBytePtr .argtypes = [ctypes .c_void_p ]
100101 self .core .CFDataGetLength .argtypes = [ctypes .c_void_p ]
102+ self .core .CGImageGetBytesPerRow .argtypes = [ctypes .c_void_p ]
103+ self .core .CGImageGetBitsPerPixel .argtypes = [ctypes .c_void_p ]
101104 self .core .CGDataProviderRelease .argtypes = [ctypes .c_void_p ]
102105 self .core .CFRelease .argtypes = [ctypes .c_void_p ]
103106
@@ -111,11 +114,14 @@ def _set_restypes(self):
111114 self .core .CGRectUnion .restype = CGRect
112115 self .core .CGDisplayRotation .restype = ctypes .c_float
113116 self .core .CGWindowListCreateImage .restype = ctypes .c_void_p
114- self .core .CGImageGetWidth .restype = ctypes .c_uint32
117+ self .core .CGImageGetWidth .restype = ctypes .c_size_t
118+ self .core .CGImageGetHeight .restype = ctypes .c_size_t
115119 self .core .CGImageGetDataProvider .restype = ctypes .c_void_p
116120 self .core .CGDataProviderCopyData .restype = ctypes .c_void_p
117121 self .core .CFDataGetBytePtr .restype = ctypes .c_void_p
118122 self .core .CFDataGetLength .restype = ctypes .c_uint64
123+ self .core .CGImageGetBytesPerRow .restype = ctypes .c_size_t
124+ self .core .CGImageGetBitsPerPixel .restype = ctypes .c_size_t
119125 self .core .CGDataProviderRelease .restype = ctypes .c_void_p
120126 self .core .CFRelease .restype = ctypes .c_void_p
121127
@@ -181,45 +187,42 @@ def grab(self, monitor):
181187 'height' : monitor [3 ] - monitor [1 ],
182188 }
183189
184- # When the monitor width is not divisible by 16, extra padding
185- # is added by macOS in the form of black pixels, which results
186- # in a screenshot with shifted pixels. To counter this, we
187- # round the width to the nearest integer divisible by 16, and
188- # we remove the extra width from the image after taking the
189- # screenshot.
190- rounded_width = math .ceil (monitor ['width' ] / 16 ) * 16
191-
192190 rect = CGRect ((monitor ['left' ], monitor ['top' ]),
193- (rounded_width , monitor ['height' ]))
191+ (monitor [ 'width' ] , monitor ['height' ]))
194192
195193 image_ref = self .core .CGWindowListCreateImage (rect , 1 , 0 , 0 )
196194 if not image_ref :
197195 raise ScreenShotError (
198196 'CoreGraphics.CGWindowListCreateImage() failed.' , locals ())
199197
200198 width = int (self .core .CGImageGetWidth (image_ref ))
201- prov = self .core .CGImageGetDataProvider (image_ref )
202- copy_data = self .core .CGDataProviderCopyData (prov )
203- data_ref = self .core .CFDataGetBytePtr (copy_data )
204- buf_len = self .core .CFDataGetLength (copy_data )
205- raw = ctypes .cast (data_ref , ctypes .POINTER (ctypes .c_ubyte * buf_len ))
206- data = bytearray (raw .contents )
207- self .core .CGDataProviderRelease (prov )
208- self .core .CFRelease (copy_data )
209-
210- if rounded_width != monitor ['width' ]:
211- data = self ._crop_width (data , monitor , width )
212-
213- return self .cls_image (data , monitor )
214-
215- @staticmethod
216- def _crop_width (image , monitor , width_to ):
217- # type: (bytearray, Dict[str, int], int) -> bytearray
218- """ Cut off the pixels from an image buffer at a particular width. """
219-
220- cropped = bytearray ()
221- for row in range (monitor ['height' ]):
222- start = row * width_to * 4
223- end = start + monitor ['width' ] * 4
224- cropped .extend (image [start :end ])
225- return cropped
199+ height = int (self .core .CGImageGetHeight (image_ref ))
200+ prov = copy_data = None
201+ try :
202+ prov = self .core .CGImageGetDataProvider (image_ref )
203+ copy_data = self .core .CGDataProviderCopyData (prov )
204+ data_ref = self .core .CFDataGetBytePtr (copy_data )
205+ buf_len = self .core .CFDataGetLength (copy_data )
206+ raw = ctypes .cast (
207+ data_ref , ctypes .POINTER (ctypes .c_ubyte * buf_len ))
208+ data = bytearray (raw .contents )
209+
210+ # Remove padding per row
211+ bytes_per_row = int (self .core .CGImageGetBytesPerRow (image_ref ))
212+ bytes_per_pixel = int (self .core .CGImageGetBitsPerPixel (image_ref ))
213+ bytes_per_pixel = (bytes_per_pixel + 7 ) // 8
214+
215+ if bytes_per_pixel * width != bytes_per_row :
216+ cropped = bytearray ()
217+ for row in range (height ):
218+ start = row * bytes_per_row
219+ end = start + width * bytes_per_pixel
220+ cropped .extend (data [start :end ])
221+ data = cropped
222+ finally :
223+ if prov :
224+ self .core .CGDataProviderRelease (prov )
225+ if copy_data :
226+ self .core .CFRelease (copy_data )
227+
228+ return self .cls_image (data , monitor , size = Size (width , height ))
0 commit comments