Page 1 of 1

Base64Encoder: Image To Binary (no disk write)

Posted: Thu Nov 11, 2010 5:44 am
by JHPJHP
Hi all,

Is it possible to do the following without writing a file to disk, another way?

*** removed the SaveImage, ReadFile, etc. ***

Code: Select all

Global ScreenWidth = GetSystemMetrics_(#SM_CXSCREEN)
Global ScreenHeight = GetSystemMetrics_(#SM_CYSCREEN)

UsePNGImageEncoder()

Procedure CaptureScreen(Left, Top, Width, Height)
  ScreenDM.DEVMODE
  ScreenDC = CreateDC_("DISPLAY", "", "", ScreenDM)
  TargetDC = CreateCompatibleDC_(ScreenDC)
  ImageHandle = CreateCompatibleBitmap_(ScreenDC, Width, Height)
  SelectObject_(TargetDC, ImageHandle)
  BitBlt_(TargetDC, 0, 0, Width, Height, ScreenDC, Left, Top, #SRCCOPY)
  DeleteDC_(TargetDC)
  ReleaseDC_(ImageHandle, ScreenDC)
  ProcedureReturn ImageHandle
EndProcedure
ScreenCaptureAddress = CaptureScreen(0, 0, ScreenWidth, ScreenHeight)
newImage = CreateImage(#PB_Any, ScreenWidth, ScreenHeight)

If IsImage(newImage)
  If StartDrawing(ImageOutput(newImage))
    DrawImage(ScreenCaptureAddress, 0, 0)
    StopDrawing()
    imageMemory = AllocateMemory(????)
    ????
    encodedImage$ = Space(MemorySize(imageMemory) * 1.5)
    Base64Encoder(imageMemory, MemorySize(imageMemory), @encodedImage$, Len(encodedImage$))
  EndIf
EndIf
FreeImage(newImage)
Thank you for any insite!

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Thu Nov 11, 2010 5:50 pm
by netmaestro
You'd be best off to save the image to a named pipe, this way you can encode the image with png or jpg for compression. If you simply save the image to memory it will be uncompressed and your base64 string will be huge. A named pipe won't go out to disk so it's pretty much the same as saving to memory anyway.

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Thu Nov 11, 2010 6:11 pm
by JHPJHP
netmaestro - thank you for showing the way:

http://www.purebasic.fr/english/viewtop ... 13&t=26380

Working on it now...

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Thu Nov 11, 2010 10:31 pm
by JHPJHP
Hi again,

The following works, but is there a way to get a more accurate imageSize before creating the DataPipe - right now I just make sure the buffer is large enough (imageSize = ScreenWidth * ScreenHeight):

Code: Select all

Global ScreenWidth = GetSystemMetrics_(#SM_CXSCREEN)
Global ScreenHeight = GetSystemMetrics_(#SM_CYSCREEN)

UsePNGImageEncoder()
UsePNGImageDecoder()

Procedure CaptureScreen(Left, Top, Width, Height)
  ScreenDM.DEVMODE
  ScreenDC = CreateDC_("DISPLAY", "", "", ScreenDM)
  TargetDC = CreateCompatibleDC_(ScreenDC)
  ImageHandle = CreateCompatibleBitmap_(ScreenDC, Width, Height)
  SelectObject_(TargetDC, ImageHandle)
  BitBlt_(TargetDC, 0, 0, Width, Height, ScreenDC, Left, Top, #SRCCOPY)
  DeleteDC_(TargetDC)
  ReleaseDC_(ImageHandle, ScreenDC)
  ProcedureReturn ImageHandle
EndProcedure
ScreenCaptureAddress = CaptureScreen(0, 0, ScreenWidth, ScreenHeight)
newImage = CreateImage(#PB_Any, ScreenWidth, ScreenHeight)

If IsImage(newImage)
  If StartDrawing(ImageOutput(newImage))
    DrawImage(ScreenCaptureAddress, 0, 0)
    StopDrawing()
    imageSize = ScreenWidth * ScreenHeight
    imageDataPipe = CreateNamedPipe_("\\.\pipe\ScreenShot", #PIPE_ACCESS_INBOUND, #PIPE_TYPE_BYTE | #PIPE_READMODE_BYTE | #PIPE_NOWAIT, 1, imageSize, imageSize, #NMPWAIT_USE_DEFAULT_WAIT, #Null)

    If imageDataPipe
      SaveImage(newImage, "\\.\pipe\ScreenShot", #PB_ImagePlugin_PNG)
      imageMemory = AllocateMemory(imageSize)
      ReadFile_(imageDataPipe, imageMemory, imageSize, @bytesRead, #Null)
      encodedImage$ = Space(MemorySize(imageMemory) * 1.5)
      Base64Encoder(imageMemory, MemorySize(imageMemory), @encodedImage$, Len(encodedImage$))
      FreeMemory(imageMemory)
      CloseHandle_(imageDataPipe)
    EndIf
  EndIf
  FreeImage(newImage)
EndIf

;Used to test with

*imageMemory = AllocateMemory(Len(encodedImage$) * 1.5)
decodedLength = Base64Decoder(@encodedImage$, Len(encodedImage$), *imageMemory, MemorySize(*imageMemory))
ReAllocateMemory(*imageMemory, decodedLength)
newImage = CatchImage(#PB_Any, *imageMemory)
SaveImage(newImage, "C:\Test.png", #PB_ImagePlugin_PNG)
FreeImage(newImage)
FreeMemory(*imageMemory)
Thank you,

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Thu Nov 11, 2010 10:50 pm
by rsts
FYI

Code tags e.g. "code" and "/code" inside square brackets[] - (see the post menu) make the code appear much nicer.

Code: Select all

Global ScreenWidth = GetSystemMetrics_(#SM_CXSCREEN)
Global ScreenHeight = GetSystemMetrics_(#SM_CYSCREEN)

UsePNGImageEncoder()
UsePNGImageDecoder()

Procedure CaptureScreen(Left, Top, Width, Height)
ScreenDM.DEVMODE
ScreenDC = CreateDC_("DISPLAY", "", "", ScreenDM)
TargetDC = CreateCompatibleDC_(ScreenDC)
ImageHandle = CreateCompatibleBitmap_(ScreenDC, Width, Height)
SelectObject_(TargetDC, ImageHandle)
cheers

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Fri Nov 12, 2010 6:24 pm
by JHPJHP
Thanks for the "Know" rsts...

Also,

srod if your reading this - can you please explain the logic behind the following bit of code:

Code: Select all

imageSize = SizeOf(BITMAPFILEHEADER) + SizeOf(BITMAPINFOHEADER)
    imageSize + 4 * ((ScreenWidth * 24 + 31) / 32) * ScreenHeight
Found @ http://www.purebasic.fr/english/viewtop ... 13&t=26380

And srod - thanks for all the leg work, and years of support (in as simple as a Google search).

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Fri Nov 12, 2010 7:05 pm
by netmaestro
A complete bitmap file consists of a BITMAPFILEHEADER followed by a BITMAPINFO structure which is comprised of a BITMAPINFOHEADER and an array of color bits. The calculation you see is the formula for finding the total number of bytes required for the colorbits in a bitmap of 24 bits depth at <screenwidth> width and <screenheight> height. All of it added together is the complete filesize of a bitmap with no compression.

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Fri Nov 12, 2010 9:03 pm
by JHPJHP
Thank you again netmaestro... the PB community owes you and an elite few a load of thanks for all the great work, past, present, and no doubt future.

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Thu Feb 23, 2012 3:32 am
by Fangbeast
I know this is an old post but..is it possible to capture the data from an imagegadget in the same way instead of an entire screen?

I'm playing with a qrcode generator from Dige and modified by Baldrick and want to capture the generated code as a png file.

The image data sits in an imagegadget and i'm not familiar enough with these advanced concepts yet.

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Thu Feb 23, 2012 5:13 am
by IdeasVacuum
You could just give the procedure Procedure CaptureScreen(Left, Top, Width, Height) the position + size of the image gadget, in screen ordinates.

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Thu Feb 23, 2012 7:36 am
by Fangbeast
IdeasVacuum wrote:You could just give the procedure Procedure CaptureScreen(Left, Top, Width, Height) the position + size of the image gadget, in screen ordinates.
Thank you, I shall try this. Shows you how little I know, graphics are one mystery I never dared try:):)

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Sat Feb 25, 2012 6:06 am
by Fangbeast
Going to look at the sample qrcode generators in the forum to see if there is a mention of the physical image size mentioned and if there is a way to get the image data rather than relying on screen co-ordinates.

#EDIT## Ah yes, the imagegadget size changes with the generated image so all I have to do is get the data from the imagegadget somehow.

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Sat Feb 25, 2012 7:27 am
by Fangbeast
Thank you for the help via PM. You know who you are:):) I am fighting with advanced concepts to expand my tiny mind.

Re: Base64Encoder: Image To Binary (no disk write)

Posted: Sat Feb 25, 2012 7:49 am
by Fangbeast
Nearly had it!! Using Dige's QRCodeGen and trying to save the image data to memory to named pipe, encode it there and debug to show I have something there.

I prettied up the code and variables so that I can follow it (bad eyes) but have trouble in the EncodeAndSave() module as I don't understand why image isn't initialised.

Code: Select all

; MS BITMAP structure
; typedef struct tagBITMAP {
;   LONG   bmType;
;   LONG   bmWidth;
;   LONG   bmHeight;
;   LONG   bmWidthBytes;
;   WORD   bmPlanes;
;   WORD   bmBitsPixel;
;   LPVOID bmBits;
;   } BITMAP, *PBITMAP;

; http://fukuchi.org/works/qrencode/index.en.html; 

UsePNGImageEncoder()
UsePNGImageDecoder()

Enumeration 1
  #Window_QRCodegen
EndEnumeration

#WindowIndex = #PB_Compiler_EnumerationValue

Enumeration 1
  #Gadget_QRCodegen_fTargetText
  #Gadget_QRCodegen_lTargetText
  #Gadget_QRCodegen_TargetText
  #Gadget_QRCodegen_fECLevel
  #Gadget_QRCodegen_lECLevel
  #Gadget_QRCodegen_ECLevel
  #Gadget_QRCodegen_lQRCodeSize
  #Gadget_QRCodegen_QRCodeSize
  #Gadget_QRCodegen_fQRCodeImage
  #Gadget_QRCodegen_QRCodeImage
  #Gadget_QRCodegen_fBase64String
  #Gadget_QRCodegen_eBase64String
  #Gadget_QRCodegen_fControl
  #Gadget_QRCodegen_bSave
  #Gadget_QRCodegen_bExit
EndEnumeration

#GadgetIndex = #PB_Compiler_EnumerationValue

Enumeration 1
  #Image_QRCodegen_QRCodeImage
  #Image_QRCodegen_bSave
  #Image_QRCodegen_bExit
EndEnumeration

#ImageIndex = #PB_Compiler_EnumerationValue

Enumeration
  #QR_ECLEVEL_L = 0 ; lowest
  #QR_ECLEVEL_M
  #QR_ECLEVEL_Q
  #QR_ECLEVEL_H     ; highest
EndEnumeration

Structure QRCode
  Version.l
  Width.l
  pSymbolData.l
EndStructure

ImportC "qrcodelib.lib"
  QRcode_encodeString8bit(Text.p-ascii, Version.l, QRecLevel.l) As "_QRcode_encodeString8bit"
  QRcode_free(*Qrcode.QRCode) As "_QRcode_free"
EndImport

CatchImage(#Image_QRCodegen_QRCodeImage,  ?_OPT_QRCodegen_QRCodeImage)
CatchImage(#Image_QRCodegen_bSave,        ?_OPT_QRCodegen_bSave)
CatchImage(#Image_QRCodegen_bExit,        ?_OPT_QRCodegen_bExit)

DataSection
  _OPT_QRCodegen_QRCodeImage  : IncludeBinary "Images\Blank.png"
  _OPT_QRCodegen_bSave        : IncludeBinary "Images\save32x32.ico"
  _OPT_QRCodegen_bExit        : IncludeBinary "Images\exit32x32.ico"
EndDataSection

Declare   CreateQRCode (content.s, ImageId.i = #PB_Any, EC_Level = #QR_ECLEVEL_L, Size = 4)
Declare   PasteQRCode()
Declare   EncodeAndSave()

Procedure CreateQRCode (content.s, ImageId.i = #PB_Any, EC_Level = #QR_ECLEVEL_L, Size = 4)
  Protected *Qrcode.QRCode, QRImg
  *Qrcode = QRcode_encodeString8bit(content, 0, EC_Level)
  With *Qrcode
    If *Qrcode = 0 Or \Width = 0
      ProcedureReturn #Null
    Else
      *mem = \pSymbolData
      w    = \Width
    EndIf
  EndWith
  QRImg  = CreateImage(ImageId.i, w + 2, w + 2)
  If QRImg
    If ImageId.i = #PB_Any
      ImageId.i = QRImg
    EndIf
  EndIf
  If StartDrawing(ImageOutput(ImageId.i))
    ; White Background
    Box (0, 0, ImageWidth(ImageId.i) + 2, ImageHeight(ImageId.i) + 2, #White) 
    ; Draw Black Dots
    For y = 0 To w - 1
      For x = 0 To w - 1
        b = PeekB(*mem) & $FF
        If b & 1
          Plot( x + 1, y + 1, #Black)
        EndIf
        *mem + 1
      Next
    Next
    StopDrawing()
    w * Size
    ResizeImage(ImageId.i, w + 2, w + 2, #PB_Image_Raw)
    ; Debug "width = " + Str(w)
    ; Debug "height = "+ Str(w)
  EndIf
  QRcode_free(*Qrcode)
  ProcedureReturn ImageId.i
EndProcedure

Procedure PasteQRCode()
  ; Create the image with parameters
  ImageId.i = CreateQRCode(GetGadgetText(#Gadget_QRCodegen_TargetText), #Null, GetGadgetState(#Gadget_QRCodegen_ECLevel), GetGadgetState(#Gadget_QRCodegen_QRCodeSize))
  ; Check if we got an image handle
  If IsImage(ImageId.i)
    ; Resize gadget to image
    ResizeGadget(#Gadget_QRCodegen_QRCodeImage, #PB_Ignore, #PB_Ignore, ImageWidth(ImageId.i), ImageHeight(ImageId.i))
    ; Set the QRCode to the image gadget
    SetGadgetState(#Gadget_QRCodegen_QRCodeImage, ImageID(ImageId.i))
    ; Show gadget is resizing with image
    ;Debug Str(GadgetWidth(3)) + "   ---   " + Str(GadgetHeight(3))
    ; Main routine needs the ImageID for saving
    ProcedureReturn ImageId.i
    ; 
  EndIf
  ; 
EndProcedure

Procedure EncodeAndSave()
  ImageHandle = GetGadgetState(#Gadget_QRCodegen_QRCodeImage)
  GetObject_(ImageHandle, SizeOf(BITMAP), @image.BITMAP)
  ImageSize = image\bmWidth * image\bmHeight
  ImageDataPipe = CreateNamedPipe_("\\.\pipe\ScreenShot", #PIPE_ACCESS_INBOUND, #PIPE_TYPE_BYTE | #PIPE_READMODE_BYTE | #PIPE_NOWAIT, 1, imageSize, imageSize, #NMPWAIT_USE_DEFAULT_WAIT, #Null)
  If ImageDataPipe
    SaveImage(ImageHandle, "\\.\pipe\ScreenShot", #PB_ImagePlugin_PNG)
    ImageMemory = AllocateMemory(ImageSize)
    ReadFile_(ImageDataPipe, ImageMemory, ImageSize, @bytesRead, #Null)
    EncodedImage.s = Space(MemorySize(ImageMemory) * 1.5)
    Base64Encoder(ImageMemory, MemorySize(ImageMemory), @EncodedImage.s, Len(EncodedImage.s))
    FreeMemory(ImageMemory)
    CloseHandle_(ImageDataPipe)
    If EncodedImage.s
      Debug EncodedImage.s
    EndIf
  EndIf
EndProcedure

If OpenWindow(#Window_QRCodegen, 300, 10, 450, 701, "2d QR Code generator and encoder", #WS_OVERLAPPEDWINDOW | #PB_Window_ScreenCentered)
  Frame3DGadget(#Gadget_QRCodegen_fTargetText, 5, 0, 440, 75, "")
  TextGadget(#Gadget_QRCodegen_lTargetText, 10, 15, 430, 25, "Text to be encoded", #PB_Text_Center)
    SetGadgetFont(#Gadget_QRCodegen_lTargetText, LoadFont(#Gadget_QRCodegen_lTargetText, "Comic Sans MS", 11, 0))
  StringGadget(#Gadget_QRCodegen_TargetText, 10, 40, 430, 25, "http://http://forums.whirlpool.net.au/forum-replies.cfm?t=1825264", #PB_String_BorderLess)
    SetGadgetFont(#Gadget_QRCodegen_TargetText, LoadFont(#Gadget_QRCodegen_TargetText, "Comic Sans MS", 11, 0))
  Frame3DGadget(#Gadget_QRCodegen_fECLevel, 5, 75, 440, 75, "")
  TextGadget(#Gadget_QRCodegen_lECLevel, 10, 90, 210, 25, "Error correcting level", #PB_Text_Center)
    SetGadgetFont(#Gadget_QRCodegen_lECLevel, LoadFont(#Gadget_QRCodegen_lECLevel, "Comic Sans MS", 11, 0))
  TrackBarGadget(#Gadget_QRCodegen_ECLevel, 10, 120, 100, 20, #QR_ECLEVEL_L, #QR_ECLEVEL_H, #PB_TrackBar_Ticks)
  TextGadget(#Gadget_QRCodegen_lQRCodeSize, 230, 90, 210, 25, "Physical size", #PB_Text_Center)
    SetGadgetFont(#Gadget_QRCodegen_lQRCodeSize, LoadFont(#Gadget_QRCodegen_lQRCodeSize, "Comic Sans MS", 11, 0))
  TrackBarGadget(#Gadget_QRCodegen_QRCodeSize, 230, 120, 100, 20, 1, 10, #PB_TrackBar_Ticks)
  SetGadgetState(#Gadget_QRCodegen_QRCodeSize, 3)
  Frame3DGadget(#Gadget_QRCodegen_fQRCodeImage, 5, 150, 440, 400, "")
  ImageGadget(#Gadget_QRCodegen_QRCodeImage, 10, 165, 80, 50, ImageID(#Image_QRCodegen_QRCodeImage), #PB_Image_Border)
  ResizeGadget(#Gadget_QRCodegen_QRCodeImage, 10, 165, 80, 50)
  ResizeImage(#Image_QRCodegen_QRCodeImage, 80, 50)
  SetGadgetState(#Gadget_QRCodegen_QRCodeImage, ImageID(#Image_QRCodegen_QRCodeImage))
  Frame3DGadget(#Gadget_QRCodegen_fBase64String, 5, 550, 440, 75, "")
  EditorGadget(#Gadget_QRCodegen_eBase64String, 10, 565, 430, 50)
  Frame3DGadget(#Gadget_QRCodegen_fControl, 5, 625, 440, 70, "")
  ButtonImageGadget(#Gadget_QRCodegen_bSave, 15, 640, 45, 45, ImageID(#Image_QRCodegen_bSave))
  ButtonImageGadget(#Gadget_QRCodegen_bExit, 390, 640, 45, 45, ImageID(#Image_QRCodegen_bExit))
  HideWindow(#Window_QRCodegen, 0)
  
  quitQRCodegen = 0
  
  PasteQRCode()                                                                           ; Generate and paste
  
  Repeat
    EventID  = WaitWindowEvent()
    MenuID   = EventMenu()
    GadgetID = EventGadget()
    WindowID = EventWindow()
    Select EventID
      Case #PB_Event_CloseWindow
        Select WindowID
          Case #Window_QRCodegen                  : quitQRCodegen = 1
        EndSelect
      Case #PB_Event_Gadget
        Select GadgetID
          Case #Gadget_QRCodegen_TargetText       : PasteQRCode()
          Case #Gadget_QRCodegen_ECLevel          : PasteQRCode()
          Case #Gadget_QRCodegen_QRCodeSize       : PasteQRCode()
          Case #Gadget_QRCodegen_bSave            : EncodeAndSave()
          Case #Gadget_QRCodegen_bExit            : quitQRCodegen = 1
        EndSelect
    EndSelect
  Until quitQRCodegen
  CloseWindow(#Window_QRCodegen)
EndIf
End