@@ -88,8 +88,8 @@ class XRRCrtcInfo(Structure):
88
88
('outputs' , POINTER (c_long )), ('rotations' , c_ushort ),
89
89
('npossible' , c_int ), ('possible' , POINTER (c_long ))]
90
90
elif system () == 'Windows' :
91
- from ctypes import byref , c_void_p , create_string_buffer , pointer , \
92
- sizeof , windll , Structure , POINTER , WINFUNCTYPE
91
+ from ctypes import c_void_p , create_string_buffer , sizeof , \
92
+ windll , Structure , POINTER , WINFUNCTYPE
93
93
from ctypes .wintypes import BOOL , DOUBLE , DWORD , HBITMAP , HDC , \
94
94
HGDIOBJ , HWND , INT , LPARAM , LONG , RECT , UINT , WORD
95
95
@@ -442,10 +442,7 @@ def enum_display_monitors(self, screen=0):
442
442
self .xrandr .XRRFreeScreenResources (mon )
443
443
444
444
def get_pixels (self , monitor ):
445
- ''' Retrieve all pixels from a monitor. Pixels have to be RGB.
446
-
447
- @TODO: this function takes the most time. Need better solution.
448
- '''
445
+ ''' Retrieve all pixels from a monitor. Pixels have to be RGB. '''
449
446
450
447
self .debug ('get_pixels' )
451
448
@@ -465,6 +462,7 @@ def get_pixels(self, monitor):
465
462
if not ximage :
466
463
raise ScreenshotError ('MSS: XGetImage() failed.' )
467
464
465
+ # @TODO: this part takes most of the time. Need a better solution.
468
466
def pix (pixel , _resultats = {}, b = pack ):
469
467
''' Apply shifts to a pixel to get the RGB values.
470
468
This method uses of memoization.
@@ -573,44 +571,50 @@ def _callback(monitor, dc, rect, data):
573
571
yield mon
574
572
575
573
def get_pixels (self , monitor ):
576
- ''' Retrieve all pixels from a monitor. Pixels have to be RGB. '''
574
+ ''' Retrieve all pixels from a monitor. Pixels have to be RGB.
575
+
576
+ [1] A bottom-up DIB is specified by setting the height to a positive number,
577
+ while a top-down DIB is specified by setting the height to a negative number.
578
+ https://msdn.microsoft.com/en-us/library/ms787796.aspx
579
+ https://msdn.microsoft.com/en-us/library/dd144879%28v=vs.85%29.aspx
580
+ '''
577
581
578
582
self .debug ('get_pixels' )
579
583
580
584
width , height = monitor [b'width' ], monitor [b'height' ]
581
585
left , top = monitor [b'left' ], monitor [b'top' ]
582
- good_width = (width * 3 + 3 ) & - 4
583
586
SRCCOPY = 0xCC0020
584
- DIB_RGB_COLORS = 0
587
+ DIB_RGB_COLORS = BI_RGB = 0
585
588
srcdc = memdc = bmp = None
586
589
587
590
try :
591
+ bmi = BITMAPINFO ()
592
+ bmi .bmiHeader .biSize = sizeof (BITMAPINFOHEADER )
593
+ bmi .bmiHeader .biWidth = width
594
+ bmi .bmiHeader .biHeight = - height # Why minus? See [1]
595
+ bmi .bmiHeader .biPlanes = 1 # Always 1
596
+ bmi .bmiHeader .biBitCount = 24
597
+ bmi .bmiHeader .biCompression = BI_RGB ;
598
+ buffer_len = height * width * 3
599
+ self .image = create_string_buffer (buffer_len )
588
600
srcdc = windll .user32 .GetWindowDC (0 )
589
601
memdc = windll .gdi32 .CreateCompatibleDC (srcdc )
590
602
bmp = windll .gdi32 .CreateCompatibleBitmap (srcdc , width , height )
591
603
windll .gdi32 .SelectObject (memdc , bmp )
592
604
windll .gdi32 .BitBlt (memdc , 0 , 0 , width , height , srcdc , left , top ,
593
605
SRCCOPY )
594
- bmi = BITMAPINFO ()
595
- bmi .bmiHeader .biSize = sizeof (BITMAPINFOHEADER )
596
- bmi .bmiHeader .biWidth = width
597
- bmi .bmiHeader .biHeight = height
598
- bmi .bmiHeader .biBitCount = 24
599
- bmi .bmiHeader .biPlanes = 1
600
- buffer_len = height * good_width
601
- pixels = create_string_buffer (buffer_len )
602
- bits = windll .gdi32 .GetDIBits (memdc , bmp , 0 , height , byref (pixels ),
603
- pointer (bmi ), DIB_RGB_COLORS )
606
+ bits = windll .gdi32 .GetDIBits (memdc , bmp , 0 , height , self .image ,
607
+ bmi , DIB_RGB_COLORS )
604
608
605
609
self .debug ('get_pixels' , 'srcdc' , srcdc )
606
610
self .debug ('get_pixels' , 'memdc' , memdc )
607
611
self .debug ('get_pixels' , 'bmp' , bmp )
608
612
self .debug ('get_pixels' , 'buffer_len' , buffer_len )
613
+ self .debug ('get_pixels' , 'len(self.image)' , len (self .image ))
609
614
self .debug ('get_pixels' , 'bits' , bits )
610
- self .debug ('get_pixels' , 'len(pixels.raw)' , len (pixels .raw ))
611
615
612
- if bits != height or len ( pixels . raw ) != buffer_len :
613
- raise ScreenshotError ('GetDIBits() failed.' )
616
+ if bits != height :
617
+ raise ScreenshotError ('MSS: GetDIBits() failed.' )
614
618
finally :
615
619
# Clean up
616
620
if srcdc :
@@ -620,47 +624,11 @@ def get_pixels(self, monitor):
620
624
if bmp :
621
625
windll .gdi32 .DeleteObject (bmp )
622
626
623
- # Note that the origin of the returned image is in the
624
- # bottom-left corner, 32-bit aligned. And it is BGR.
625
- # Need to "arrange" that.
626
- return self ._arrange (pixels .raw , good_width , height )
627
-
628
- def _arrange (self , data , width , height ):
629
- ''' Reorganises data when the origin of the image is in the
630
- bottom-left corner and converts BGR triple to RGB. '''
631
-
632
- self .debug ('_arrange' )
633
-
634
- total = width * height
635
- scanlines = [b'0' ] * total
636
- # Here we do the same thing but in Python 3, the use of struct.pack
637
- # slowns down the process by a factor of 2.5 or more.
638
- if sys .version < '3' :
639
- for y in range (height ):
640
- shift = width * (y + 1 )
641
- offset = total - shift
642
- for x in range (0 , width - 2 , 3 ):
643
- off = offset + x
644
- scanlines [shift + x :shift + x + 3 ] = \
645
- data [off + 2 ], data [off + 1 ], data [off ]
646
- else :
647
- def pix (pixel , _resultats = {}, b = pack ):
648
- ''' Apply conversion to a pixel to get the right value.
649
- This method uses of memoization.
650
- '''
651
- if pixel not in _resultats :
652
- _resultats [pixel ] = b (b'<B' , pixel )
653
- return _resultats [pixel ]
654
-
655
- for y in range (height ):
656
- shift = width * (y + 1 )
657
- offset = total - shift
658
- for x in range (0 , width - 2 , 3 ):
659
- off = offset + x
660
- scanlines [shift + x :shift + x + 3 ] = \
661
- pix (data [off + 2 ]), pix (data [off + 1 ]), pix (data [off ])
662
-
663
- return b'' .join (scanlines )
627
+ # Replace pixels values: BGR to RGB
628
+ # @TODO: this part takes most of the time. Need a better solution.
629
+ for idx in range (0 , buffer_len - 2 , 3 ):
630
+ self .image [idx + 2 ], self .image [idx ] = self .image [idx ], self .image [idx + 2 ]
631
+ return self .image
664
632
665
633
666
634
def main ():
0 commit comments