Page 1 of 1

GrapDrawingImage()

Posted: Tue Apr 07, 2015 10:32 pm
by WilliamL
This code is supposed to grab the entire active window and save it to a file. I get a file which is the correct dimensions but it is of only the 'save' button greatly enlarged. What am I doing wrong or is a problem with 5.31x64?

Code: Select all

#wndw=1
#gadget1=1
#gadget2=2

Procedure SaveWindow(wndid)
    Define imageid
   
    w=WindowWidth(wndid)
    h=WindowHeight(wndid)
    
    If CreateImage(imageid,w,h)
        If StartDrawing(WindowOutput(wndid))
            imageid = GrabDrawingImage(#PB_Any,0,0,w,h)
            StopDrawing()
        EndIf
    Else
        MessageRequester("imageid","no image created")
    EndIf
    
    
    filename$ = SaveFileRequester("Saving 'Test' as jpg.", "Test.jpg",".jpg",0)
    If filename$
        UseJPEGImageEncoder()
        SaveImage(imageid,filename$ ,#PB_ImagePlugin_JPEG)
    EndIf

    FreeImage(imageid)
EndProcedure

w=200
h=264
If OpenWindow(#wndw,0,0,w,h, "test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
    ButtonGadget(#gadget2, 10, 10, 100, 30, "save")
    ListViewGadget(#gadget1,4,210,w-8,50)
    AddGadgetItem(#gadget1,-1,"Line 1")       

    Repeat
        EventID = WaitWindowEvent()
        If EventID = #PB_Event_Gadget
            If EventGadget() = #gadget2
                SaveWindow(#wndw)  
            EndIf
        EndIf             
    Until EventID = #PB_Event_CloseWindow
EndIf

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 8:07 am
by wilbert
I don't know what is wrong but this writes a png

Code: Select all

ImportC ""
  CGWindowListCreateImage(x.CGFloat, y.CGFloat, w.CGFloat, h.CGFloat, windowOption, windowID, imageOption)
  CGImageRelease(image)
EndImport

Procedure SaveWindow(Wnd, IgnoreFraming = #True)
  Protected FileName.s = SaveFileRequester("Saving window as png.", "Test.png", ".png", 0)
  If FileName
    Protected WindowImage = CGWindowListCreateImage(0, 0, 0, 0, 8, CocoaMessage(0, WindowID(Wnd), "windowNumber"), IgnoreFraming & 1)
    If WindowImage
      Protected ImageRep = CocoaMessage(0, CocoaMessage(0, 0, "NSBitmapImageRep alloc"), "initWithCGImage:", WindowImage)
      Protected PngData = CocoaMessage(0, ImageRep, "representationUsingType:", #NSPNGFileType, "properties:", #nil)
      CocoaMessage(0, PngData, "writeToFile:$", @FileName, "atomically:", #YES)
      CocoaMessage(0, ImageRep, "release")
      CGImageRelease(WindowImage)
    EndIf
  EndIf
EndProcedure
Returning a PB image

Code: Select all

ImportC ""
  CGWindowListCreateImage(x.CGFloat, y.CGFloat, w.CGFloat, h.CGFloat, windowOption, windowID, imageOption)
  CGImageRelease(image)
EndImport

Procedure.i GrabWindow(Wnd, IgnoreFraming = #True)
  Protected.i Result, WindowImage, ImageObj, Size.NSSize
  WindowImage = CGWindowListCreateImage(0, 0, 0, 0, 8, CocoaMessage(0, WindowID(Wnd), "windowNumber"), IgnoreFraming & 1)
  If WindowImage
    ImageObj = CocoaMessage(0, CocoaMessage(0, 0, "NSImage alloc"), "initWithCGImage:", WindowImage, "size:@", @Size)
    CocoaMessage(@Size, ImageObj, "size")
    If Size\width And Size\height
      Result = CreateImage(#PB_Any, Size\width, Size\height, 32, #PB_Image_Transparent)
      If Result
        StartDrawing(ImageOutput(Result))
        DrawAlphaImage(ImageObj, 0, 0)
        StopDrawing()
      EndIf
    EndIf
    CocoaMessage(0, ImageObj, "release")
    CGImageRelease(WindowImage)
  EndIf
  ProcedureReturn Result
EndProcedure

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 9:00 am
by Shardik
WilliamL wrote:This code is supposed to grab the entire active window and save it to a file. I get a file which is the correct dimensions but it is of only the 'save' button greatly enlarged. What am I doing wrong or is a problem with 5.31x64?
In this thread luis already described the solution for your problem: your grabbed image is 32 bit and the alpha component is set to fully transparent for the background so you have to set it to fully opaque. Therefore you have to insert the following part into to your code after StopDrawing():

Code: Select all

            If StartDrawing(ImageOutput(imageid))
                DrawingMode(#PB_2DDrawing_AlphaChannel)
                Box(0, 0, w, h, RGBA(0, 0, 0, 255))   
                StopDrawing()
            EndIf
and you should obtain the complete contents of your window. Unfortunately I am currently at work and can't test this on MacOS but I have tested it successfully on Windows XP, Windows 8.1 x64 and Ubuntu 14.04 x86 with Unity.

To make the testing easier I have written a new example which displays the grabbed window directly beneath the source window:

Code: Select all

EnableExplicit

Procedure GrabWindow(WindowID.I)
  Protected ImageGadgetID.I
  Protected ImageID.I
  Protected WindowHeight = WindowHeight(WindowID)
  Protected WindowWidth = WindowWidth(WindowID)

  If CreateImage(0, WindowWidth, WindowHeight)
    OpenWindow(#PB_Any, WindowX(WindowID) + WindowWidth + 20, WindowY(WindowID),
      WindowWidth, WindowHeight, "Grabbed Window")
    ImageGadgetID = ImageGadget(#PB_Any, 0, 0, WindowHeight, WindowWidth, 0)

    ; ---- Flush events (otherwise the button will appear as depressed)
    While WindowEvent() : Wend

    If StartDrawing(WindowOutput(WindowID))
      ImageID = GrabDrawingImage(#PB_Any, 0, 0, WindowWidth, WindowHeight)
      StopDrawing()

      If StartDrawing(ImageOutput(ImageID))
        DrawingMode(#PB_2DDrawing_AlphaChannel)
        Box(0, 0, WindowWidth, WindowHeight, RGBA(0, 0, 0, 255))   
        StopDrawing()
        SetGadgetState(ImageGadgetID, ImageID(ImageID))
      EndIf
    EndIf
  EndIf
EndProcedure

OpenWindow(0, 100, 100, 200, 264, "Original window")
ButtonGadget(0, 10, 10, 100, 30, "Grab window")
ListViewGadget(1, 10, 210, WindowWidth(0) - 20, 50)
AddGadgetItem(1, -1, "Line 1")       

Repeat
  Select WaitWindowEvent()
    Case #PB_Event_CloseWindow
      Break
    Case #PB_Event_Gadget
      If EventGadget() = 0
        GrabWindow(0) 
      EndIf
  EndSelect             
ForEver

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 5:23 pm
by WilliamL
Hi Shardik and wilbert,

You guys are the best! I really appreciate how you come thru every time!

I got my original code from the thread Shardik refers to and my problem isn't that the image is transparent but that is is highly magnified to the point that only the button is partially showing (and duplicated!). You can see below what I am talking about. I tried flushing the events before grabbing the image but that didn't help. I would be interested to know what you would get if you tried my example?
Image

Oh, something just occurred to me... I wonder if, since I'm using a retina display, the resolution (dpi) is doubled and that is throwing off the size?

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 5:31 pm
by WilliamL
Hey, great news!

I used

Code: Select all

    w=WindowWidth(wndid)*2
    h=WindowHeight(wndid)*2
and it worked perfectly! Well, the pict is twice as big but I suppose I could resize it. ;-)

Now I know to use this work-around but there will need to be a PB 'awareness' of this retina display problem (hint Fred).

Come to think of it, I will need to be able to discern which type of display the app is on since the other computer is not a retina. Is there any way to tell which type of display the computer has?

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 5:41 pm
by wilbert
I see you already answered your own question.
Anyway, this is what the captures look like on my OSX without retina display.
Shardik code . wrote:Image
My code IgnoreFraming = #True . wrote:Image
My code IgnoreFraming = #False . wrote:Image

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 6:33 pm
by WilliamL
@wilbert,

I tried your cocoa code and it worked fine but the resulting image was twice the original size. Your code is easier but if I want it the original size I will still need to know whether to reduce the size or not. I was also not able to figure out the size and image id to use the ResizeImage() command.

Code: Select all

ResizeImage(imageid,ImageWidth(WindowImage)/2,ImageHeight(WindowImage)/2) ; must reduce by 2 with retina display
This code give me an 'image not initalised' error.

One thing that puzzles me. If I use my code (with out doubling the w/h sizes) and send it to the printer it works fine but the image is shifted down and right about the image size. :shock: Well, this is for another day....

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 7:01 pm
by wilbert
This should check retina

Code: Select all

OpenWindow(0, 0, 0, 100, 50, "Test", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)

Retina = #False
If OSVersion() >= #PB_OS_MacOSX_10_7
  CocoaMessage(@ScaleFactor.CGFloat, WindowID(0), "backingScaleFactor")
  If ScaleFactor > 1
    Retina = #True
  EndIf
EndIf

If Retina
  Debug "Retina"
Else
  Debug "No retina"
EndIf

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 7:49 pm
by WilliamL
Thanks wilbert, that will let me get on with my project. :)

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 7:57 pm
by wilbert
WilliamL wrote:Thanks wilbert, that will let me get on with my project. :)
Since ScalingFactor contains exactly what you need for resizing, you can probably use it like this

Code: Select all

If OSVersion() >= #PB_OS_MacOSX_10_7
  CocoaMessage(@ScaleFactor.CGFloat, WindowID(0), "backingScaleFactor")
  If ScaleFactor > 1
    ResizeImage(WindowImage, ImageWidth(WindowImage)/ScaleFactor, ImageHeight(WindowImage)/ScaleFactor)
  EndIf
EndIf

Re: GrapDrawingImage()

Posted: Wed Apr 08, 2015 9:06 pm
by WilliamL
@wilbert,

Yes, I like using a variable like that. Very efficient!

Code: Select all

  CocoaMessage(@ScaleFactor.CGFloat, WindowID(0), "backingScaleFactor") ; 1=std  2=retina
    w=WindowWidth(wndid)*scalefactor
    h=WindowHeight(wndid)*scalefactor
   ...
    ResizeImage(WindowImage, ImageWidth(WindowImage)/ScaleFactor, ImageHeight(WindowImage)/ScaleFactor)