mirror of
https://gitlab.com/freepascal.org/lazarus/lazarus.git
synced 2025-06-27 00:08:26 +02:00
208 lines
6.5 KiB
ObjectPascal
208 lines
6.5 KiB
ObjectPascal
{
|
|
* Converted from glGrab.c by Felipe Monteiro de Carvalho
|
|
* Found in http://lists.apple.com/archives/cocoa-dev/2005/Aug/msg00901.html
|
|
* Line 186 (from the original C source)
|
|
has a fix for Intel processors: http://lists.apple.com/archives/quartz-dev/2006/May/msg00100.html
|
|
|
|
*****************************************************************************
|
|
This file is part of the Lazarus Component Library (LCL)
|
|
|
|
See the file COPYING.modifiedLGPL.txt, included in this distribution,
|
|
for details about the license.
|
|
*****************************************************************************
|
|
}
|
|
unit glgrab;
|
|
|
|
{$ifdef fpc}{$mode objfpc}{$H+}{$endif}
|
|
|
|
interface
|
|
|
|
{#import <CoreFoundation/CoreFoundation.h>
|
|
#import <ApplicationServices/ApplicationServices.h>
|
|
#import <OpenGL/OpenGL.h>
|
|
#import <OpenGL/gl.h>}
|
|
uses
|
|
MacOSAll,
|
|
GL, OpenGL;
|
|
|
|
function grabViaOpenGL(display: CGDirectDisplayID; srcRect: CGRect): CGImageRef;
|
|
|
|
implementation
|
|
|
|
{ Missing constants from FPCMacOSAll }
|
|
const
|
|
kCGNullDirectDisplay = CGDirectDisplayID(0);
|
|
|
|
{
|
|
* perform an in-place swap from Quadrant 1 to Quadrant III format
|
|
* (upside-down PostScript/GL to right side up QD/CG raster format)
|
|
* We do this in-place, which requires more copying, but will touch
|
|
* only half the pages. (Display grabs are BIG!)
|
|
*
|
|
* Pixel reformatting may optionally be done here if needed.
|
|
}
|
|
procedure swizzleBitmap(data: Pointer; rowBytes, height: Integer);
|
|
var
|
|
top, bottom: Integer;
|
|
buffer: Pointer;
|
|
topP: Pointer;
|
|
bottomP: Pointer;
|
|
base: Pointer;
|
|
begin
|
|
top := 0;
|
|
bottom := height - 1;
|
|
base := data;
|
|
buffer := System.GetMem(rowBytes);
|
|
|
|
while ( top < bottom ) do
|
|
begin
|
|
topP := {%H-}Pointer((top * rowBytes) + {%H-}PtrInt(base));
|
|
bottomP := {%H-}Pointer((bottom * rowBytes) + {%H-}PtrInt(base));
|
|
|
|
{
|
|
* Save and swap scanlines.
|
|
*
|
|
* This code does a simple in-place exchange with a temp buffer.
|
|
* If you need to reformat the pixels, replace the first two bcopy()
|
|
* calls with your own custom pixel reformatter.
|
|
}
|
|
System.Move(topP^, buffer^, rowBytes );
|
|
System.Move(bottomP^, topP^, rowBytes );
|
|
System.Move(buffer^, bottomP^, rowBytes );
|
|
|
|
Inc(top);
|
|
Dec(bottom);
|
|
end;
|
|
System.FreeMem( buffer );
|
|
end;
|
|
|
|
{
|
|
* Given a display ID and a rectangle on that display, generate a CGImageRef
|
|
* containing the display contents.
|
|
*
|
|
* srcRect is display-origin relative.
|
|
*
|
|
* This function uses a full screen OpenGL read-only context.
|
|
* By using OpenGL, we can read the screen using a DMA transfer
|
|
* when it's in millions of colors mode, and we can correctly read
|
|
* a microtiled full screen OpenGL context, such as a game or full
|
|
* screen video display.
|
|
*
|
|
* Returns a CGImageRef. When you are done with the CGImageRef, release it
|
|
* using CFRelease().
|
|
* Returns NULL on an error.
|
|
}
|
|
function grabViaOpenGL(display: CGDirectDisplayID; srcRect: CGRect): CGImageRef;
|
|
var
|
|
bitmap: CGContextRef;
|
|
image: CGImageRef;
|
|
data: Pointer;
|
|
bytewidth: Longint;
|
|
width, height: GLint;
|
|
cSpace: CGColorSpaceRef;
|
|
|
|
glContextObj: CGLContextObj;
|
|
pixelFormatObj: CGLPixelFormatObj;
|
|
numPixelFormats: Longint;
|
|
attribs: array[0..3] of CGLPixelFormatAttribute =
|
|
(
|
|
kCGLPFAFullScreen,
|
|
kCGLPFADisplayMask,
|
|
CGLPixelFormatAttribute(kCGLPFAAllRenderers), { Display mask bit goes here }
|
|
CGLPixelFormatAttribute(1) // dummy value, workaround for fpc
|
|
);
|
|
begin
|
|
cSpace := CGColorSpaceCreateWithName (kCGColorSpaceGenericRGB);
|
|
|
|
if ( display = kCGNullDirectDisplay ) then
|
|
display := CGMainDisplayID();
|
|
attribs[2] := CGLPixelFormatAttribute(CGDisplayIDToOpenGLDisplayMask(display));
|
|
// last value must be 0
|
|
PLongint(@attribs[3])^ := 0;
|
|
|
|
{ Build a full-screen GL context }
|
|
CGLChoosePixelFormat( attribs, @pixelFormatObj, @numPixelFormats );
|
|
if ( pixelFormatObj = nil ) then // No full screen context support
|
|
Exit(nil);
|
|
CGLCreateContext( pixelFormatObj, nil, @glContextObj ) ;
|
|
CGLDestroyPixelFormat( pixelFormatObj ) ;
|
|
if ( glContextObj = nil ) then
|
|
Exit(nil);
|
|
|
|
|
|
CGLSetCurrentContext( glContextObj ) ;
|
|
CGLSetFullScreen( glContextObj ) ;
|
|
|
|
|
|
glReadBuffer(GL_FRONT);
|
|
|
|
|
|
width := Round(srcRect.size.width);
|
|
height := Round(srcRect.size.height);
|
|
|
|
|
|
bytewidth := width * 4; // Assume 4 bytes/pixel for now
|
|
bytewidth := (bytewidth + 3) and (not 3); // Align to 4 bytes
|
|
//bytes := bytewidth * height; // width * height
|
|
|
|
{ Build bitmap context }
|
|
data := System.GetMem(height * bytewidth);
|
|
if ( data = nil ) then
|
|
begin
|
|
CGLSetCurrentContext( nil );
|
|
CGLClearDrawable( glContextObj ); // disassociate from full screen
|
|
CGLDestroyContext( glContextObj ); // and destroy the context
|
|
Result := nil;
|
|
Exit;
|
|
end;
|
|
bitmap := CGBitmapContextCreate(data, width, height, 8, bytewidth,
|
|
cSpace, kCGImageAlphaNoneSkipFirst { XRGB });
|
|
CFRelease(cSpace);
|
|
|
|
|
|
{ Read framebuffer into our bitmap }
|
|
glFinish(); { Finish all OpenGL commands }
|
|
glPixelStorei(GL_PACK_ALIGNMENT, 4); { Force 4-byte alignment }
|
|
glPixelStorei(GL_PACK_ROW_LENGTH, 0);
|
|
glPixelStorei(GL_PACK_SKIP_ROWS, 0);
|
|
glPixelStorei(GL_PACK_SKIP_PIXELS, 0);
|
|
|
|
{
|
|
* Fetch the data in XRGB format, matching the bitmap context.
|
|
}
|
|
glReadPixels(Round(srcRect.origin.x), Round(srcRect.origin.y), width, height,
|
|
GL_BGRA,
|
|
{$ifdef __BIG_ENDIAN__}
|
|
GL_UNSIGNED_INT_8_8_8_8_REV, // for PPC
|
|
{$else}
|
|
GL_UNSIGNED_INT_8_8_8_8, // for Intel! http://lists.apple.com/archives/quartz-dev/2006/May/msg00100.html
|
|
{$endif}
|
|
data);
|
|
{
|
|
* glReadPixels generates a quadrant I raster, with origin in the lower left
|
|
* This isn't a problem for signal processing routines such as compressors,
|
|
* as they can simply use a negative 'advance' to move between scanlines.
|
|
* CGImageRef and CGBitmapContext assume a quadrant III raster, though, so we need to
|
|
* invert it. Pixel reformatting can also be done here.
|
|
}
|
|
swizzleBitmap(data, bytewidth, height);
|
|
|
|
{ Make an image out of our bitmap; does a cheap vm_copy of the bitmap }
|
|
image := CGBitmapContextCreateImage(bitmap);
|
|
|
|
{ Get rid of bitmap }
|
|
CFRelease(bitmap);
|
|
System.FreeMem(data);
|
|
|
|
{ Get rid of GL context }
|
|
CGLSetCurrentContext( nil );
|
|
CGLClearDrawable( glContextObj ); // disassociate from full screen
|
|
CGLDestroyContext( glContextObj ); // and destroy the context
|
|
|
|
{ Returned image has a reference count of 1 }
|
|
Result := image;
|
|
end;
|
|
|
|
end.
|
|
|