[Implemented] JPG Image Display

Got an idea for enhancing PureBasic? New command(s) you'd like to see?
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

[Implemented] JPG Image Display

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

My #1 feature request:

PLEASE! make PB Image commands support JPG images as well as BMP and ICO. Most of my image-handling requires JPG images, and converting to BMP format isn't a possibility. PB already supports reading and displaying JPG images as sprites; would it be difficult to allow them to be displayed in the standard ImageGadget? Am I missing something obvious that can be done with a sprite to display it in a standard Windows app?

Thanks for listening!

Regards,
Eric
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

Hi,

UPDATE: updated code for 3.20.

This is just for testing purposes, not a complete app, and you'll notice there are lots of features missing; but it loads and shows jpeg and gif files. Enjoy it;

Code: Select all

;
; Picture View for PB Windows
;
; Written in PureBasic by El_Choni
;
; Thanks: Rings for helping me to deal with bitmaps in memory
;

version.s = "0.1"

Structure ImageData
  width.l
  height.l
  colors.l
  chars.l
  bits.b
EndStructure

Global IData.ImageData, BmpSize, BmpImage, FileSize, loaded.b, WindowWidth, WindowHeight, ImgWidth, ImgHeight, MenuHeight, xImg, yImg, ParentHandle, ptMinTrackSizex, aspect.f, hDC

*BmpFile.BITMAPFILEHEADER
*BmpInfo.BITMAPINFOHEADER
bmp.BITMAP

#BI_RGB = 0

Global File.s, gpPicture

gpPicture = 0
pstm = 0

#E_POINTER = $80000005
#E_NOINTERFACE = $80000004
#E_OUTOFMEMORY = $80000002

#IPicture_Release = 8
#IPicture_Get_Width = 24
#IPicture_Get_Height = 28
#IPicture_Render = 32

Global lphmwidth, lphmheight

ptMinTrackSizex = 320

Global nwidth, nheight, hmheight, hmwidth, rc.RECT

Procedure UpdateImage()
  If loaded
    ImgHeight = WindowHeight
    ImgWidth = ImgHeight*aspect
    If ImgWidth>WindowWidth
      ImgWidth = WindowWidth
      ImgHeight = ImgWidth/aspect
    EndIf
    If WindowWidth=ptMinTrackSizex And ImgWidth> 16
        UpdateImage()
        If loaded
          SetWindowText_(ParentHandle, "Picture View - "+File+" "+StrF(((ImgWidth/IData\width)*100), 2)+"%")
        EndIf
      EndIf
    Case #WM_PAINT
      result = UpdateImage()
  EndSelect
  ProcedureReturn result
EndProcedure

Main:

tab.s = Chr(9)
open.s = tab+"Ctrl+O"
save.s = tab+"Ctrl+S"
tabquit.s = tab+"Ctrl+Q"
cien.s = tab+"Ctrl+Alt+0"
zoomin.s = tab+"+"
zoomout.s = tab+"-"
copy.s = tab+"Ctrl+C"
paste.s = tab+"Ctrl+V"

#Open = 0
#Save = 1
#Quit = 2
#Actual = 3
#Zoom_In = 4
#Zoom_Out = 5
#Copy = 6
#Paste = 7

OleInitialize_(0)

If OpenWindow(0, 100, 100, 480, 320, #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_SizeGadget, "Little Image Tool")
  ParentHandle = WindowID()
  If CreateMenu(0, ParentHandle)
    MenuTitle("&File")
      MenuItem(#Open, "&Open"+open)
      MenuBar()
      MenuItem(#Save, "&Save as..."+save)
      MenuBar()
      MenuItem(#Quit, "&Quit"+tabquit)
    MenuTitle("&Tools")
      MenuItem(#Actual, "100%"+cien)
      MenuItem(#Zoom_In, "Zoom in"+zoomin)
      MenuItem(#Zoom_Out, "Zoom out"+zoomout)
      MenuBar()
      MenuItem(#Copy, "&Copy"+copy)
      MenuItem(#Paste, "Paste"+paste)
  EndIf

  DragAcceptFiles_(ParentHandle, #TRUE)

  MenuHeight = GetSystemMetrics_(#SM_CYCAPTION)+GetSystemMetrics_(#SM_CYMENU)+8
  Brush.LOGBRUSH\lbColor = 0
  SetClassLong_(ParentHandle, #GCL_HBRBACKGROUND, CreateBrushIndirect_(Brush))

  If CreateGadgetList(ParentHandle)
    CreateImage(0, WindowWidth(), WindowHeight())
    ImageGadget = ImageGadget(0, 0, MenuHeight, WindowWidth(), WindowHeight()+MenuHeight, UseImage(0))
    hDC = GetDC_(ImageGadget)

    WindowWidth = WindowWidth()
    WindowHeight = WindowHeight()

    SetWindowCallback(@ResizeCallback())

    File = ProgramParameter()
    Gosub OpenFile
    
    SetForegroundWindow_(ParentHandle)

    AddKeyboardShortcut(0, #PB_Shortcut_O|#PB_Shortcut_Control, #Open)
    AddKeyboardShortcut(0, #PB_Shortcut_S|#PB_Shortcut_Control, #Save)
    AddKeyboardShortcut(0, #PB_Shortcut_Q|#PB_Shortcut_Control, #Quit)
    AddKeyboardShortcut(0, #PB_Shortcut_Pad0|#PB_Shortcut_Alt|#PB_Shortcut_Control, #Actual)
    AddKeyboardShortcut(0, #PB_Shortcut_Add, #Zoom_In)
    AddKeyboardShortcut(0, #PB_Shortcut_Subtract, #Zoom_Out)
    AddKeyboardShortcut(0, #PB_Shortcut_C|#PB_Shortcut_Control, #Copy)
    AddKeyboardShortcut(0, #PB_Shortcut_V|#PB_Shortcut_Control, #Paste)
 
    Repeat

      Select WaitWindowEvent()
        Case #PB_EventMenu
          Select EventMenuID()
            Case #Open
              Gosub Open
            Case #Save
              Gosub Save
            Case #Quit
              Quit = 1
            Case #Actual
              Gosub ActualPixels
            Case #Zoom_In
              Gosub ZoomIn
            Case #Zoom_Out
              Gosub ZoomOut
            Case #Copy
              Gosub Copy
            Case #Paste
              Gosub Paste
          EndSelect
        Case #WM_DROPFILES
          AllocateMemory(3, 256, 0)
          hDrop = EventwParam()
          DragQueryFile_(hDrop, 0, MemoryID(), 256)
          File = PeekS(MemoryID())
          FreeMemory(3)
          DragFinish_(hDrop)
          Gosub OpenFile
        Case #PB_EventCloseWindow
          Quit = 1
      EndSelect
    Until Quit
  EndIf
EndIf
OleUninitialize_()

End

;
; Actual pixels (100%)
;

ActualPixels:

If loaded
  ResizeWindow(nwidth, nheight+MenuHeight)
  SetWindowText_(ParentHandle, "Picture View - "+File+" 100%")
EndIf

Return

;
; Zoom out
;

ZoomOut:

If loaded
  ResizeWindow(ImgWidth/2, (ImgHeight/2)+MenuHeight)
  delayed = 0
EndIf

Return

;
; Zoom in
;

ZoomIn:

If loaded
  ResizeWindow(ImgWidth*2, (ImgHeight*2)+MenuHeight)
  delayed = 0
EndIf

Return

;
; Open image directly
;

OpenFile:
 
extension.s = LCase(Right(File, 4))
If extension=".gif" Or extension=".jpe" Or extension=".jpg" Or extension=".bmp"
  Gosub OpenPicture
  Gosub ShowImage
  Gosub ActualPixels
EndIf

Return

;
; Open image request
;

Open:
 
File = OpenFileRequester("Open image file","","Windows BMP Image (*.bmp)|*.bmp|GIF Image (*.gif)|*.gif|Jpeg Image (*.jpg)|*.jpe;*.jpg", 0)
extension.s = LCase(Right(File, 4))
If extension=".gif" Or extension=".jpe" Or extension=".jpg" Or extension=".bmp"
  Gosub OpenPicture
  Gosub ShowImage
  Gosub ActualPixels
EndIf

Return

;
; Save
;

Save:
 
If loaded
  File = SaveFileRequester("Save image file","","Windows BMP Image (*.bmp)|*.bmp", 0)
  If Len(File)>0
    SaveImage(0, File) 
  EndIf
EndIf

Return

;
; Copy image to clipboard
;

Copy:

If loaded
  SetClipboardData(#PB_ClipboardImage, ImageID())
EndIf

Return

;
; Paste image
;

Paste:

OpenClipboard_(ParentHandle)
If IsClipboardFormatAvailable_(#CF_BITMAP)
  If loaded
    DeleteObject_(iHwnd)
    FreeMemory(0)
  Else
    FreeImage(0)
  EndIf
  ciHwnd = GetClipboardData_(#CF_BITMAP)
  GetObject_(ciHwnd, SizeOf(BITMAP), @bmp)
  SelectObject_(hDC, ciHwnd)
  IData\width = bmp\bmWidth
  IData\height = bmp\bmHeight
  IData\bits = bmp\bmBitsPixel*bmp\bmPlanes
  pad = (bmp\bmWidthBytes-IData\width)*IData\height
  iHwnd = CreateCompatibleBitmap_(hDC, IData\width, IData\height)
  loaded = 1
  aspect = IData\width/IData\height
  RGBQuad = 0
  If IData\bits>3))+pad
  BmpImage = AllocateMemory(0, BmpSize, 0)
  *BmpInfo = BmpImage+SizeOf(BITMAPFILEHEADER)
  *BmpInfo\biSize = SizeOf(BITMAPINFOHEADER)
  *BmpInfo\biWidth = IData\width
  *BmpInfo\biHeight = IData\height
  *BmpInfo\biPlanes = 1
  *BmpInfo\biBitCount = IData\bits
  *BmpInfo\biCompression = #BI_RGB; Currently supports only uncompressed images
  GetDIBits_(hDC, ciHwnd, 0, IData\height, BMPImage+SizeOf(BITMAPFILEHEADER)+SizeOf(BITMAPINFOHEADER)+2+RGBQuad, BMPImage+SizeOf(BITMAPFILEHEADER), #DIB_RGB_COLORS)
  CopyMemory(*BmpInfo, BmpImage+SizeOf(BITMAPFILEHEADER), SizeOf(BITMAPINFOHEADER)+RGBQuad+2)
  FreeMemory(1)
  *BmpFile = BmpImage
  *BmpFile\bfType = 19778
  *BmpFile\bfOffBits = SizeOf(BITMAPFILEHEADER)+SizeOf(BITMAPINFOHEADER)+RGBQuad+2
  *BmpFile\bfSize = BmpSize
  *BmpFile\bfReserved1 = 0
  *BmpFile\bfReserved2 = 0
  SetDIBits_(hDC, iHwnd, 0, IData\height, BmpImage+*BmpFile\bfOffBits, BmpImage+SizeOf(BITMAPFILEHEADER), 0)
  ResizeWindow(IData\width, IData\height+MenuHeight)
EndIf
CloseClipboard_()

Return

;
; Open image file
;

OpenPicture:

If ReadFile(0, File)
  LoadLibrary_("OLEPRO32.DLL")
  LoadLibrary_("ADVAPI32.DLL")
  LoadLibrary_("OLEAUT32.DLL")
  FileSize = FileSize(File)
  If loaded
    FreeMemory(0)
  EndIf
  hGlobal = GlobalAlloc_(#GMEM_MOVEABLE, FileSize);
  pvData = GlobalLock_(hGlobal)
  ReadData(pvData, FileSize)
  CloseFile(0)
  GlobalUnlock_(hGlobal)
  CreateStreamOnHGlobal_(hGlobal, #TRUE, @pstm)
  If gpPicture
    CallCOM(#IPicture_Release, gpPicture)
  EndIf
  Select OleLoadPicture_(pstm, FileSize, #FALSE, ?IID_IPicture, @gpPicture)
    Case #E_POINTER
      Error = 222
    Case #E_NOINTERFACE
      Error = 223
    Case #E_OUTOFMEMORY
      Error = 224
    Default
      Error = 0
  EndSelect
  CallCOM(#IPicture_Release, pstm)
  GlobalFree_(hGlobal)
  xscreenpixels = GetDeviceCaps_(hDC, #LOGPIXELSX)
  yscreenpixels = GetDeviceCaps_(hDC, #LOGPIXELSY)
  CallCOM(#IPicture_Get_Width, gpPicture, @hmwidth)
  CallCOM(#IPicture_Get_Height, gpPicture, @hmheight)
  nwidth = (hmwidth*xscreenpixels)/2540
  nheight = (hmheight*yscreenpixels)/2540
  ResizeGadget(0, 0, 0, nwidth, nheight)
  ResizeWindow(nwidth, nheight+MenuHeight)
  rc.RECT
  GetClientRect_(ImageGadget, @rc)
  ps.PAINTSTRUCT
  ps\hdc = hDC
  ps\fErase = 1
  ps\rcPaint = @rc
  aspect = nwidth/nheight
  loaded = 1
  UpdateImage()
EndIf

Return

;
; Save Bmp file
;

SaveBmp:

If CreateFile(0, File)
  WriteData(BmpImage, *BmpFile\bfSize)
  CloseFile(0)
EndIf

Return

;
; Show image
;

ShowImage:

Return

If BmpImage
  If loaded
    DeleteObject_(iHwnd)
  Else
    FreeImage(0)
  EndIf
  ResizeWindow(IData\width, IData\height+MenuHeight)
  iHwnd = CreateCompatibleBitmap_(GetDC_(ParentHandle), IData\width, IData\height)
  DestroyWindow_(ImageGadget)
  ImageGadget = ImageGadget(0, 0, MenuHeight, IData\width, IData\height, iHwnd)
  hDC = GetDC_(ImageGadget)
  SetDIBits_(hDC, iHwnd, 0, IData\height, BmpImage+*BmpFile\bfOffBits, BmpImage+SizeOf(BITMAPFILEHEADER), 0)
  loaded = 1
  aspect = IData\width/IData\height
  SetForegroundWindow_(ParentHandle)
  UpdateImage()
EndIf

Return

![section .data]
IID_IPicture:
!dd $07bf80980
!dw $0bf32, $0101a
!db $08b, $0bb, 0, $0aa, 0, $030, $0c, $0ab
Bye,

El_Choni

Edited by - El_Choni on 01 July 2002 01:16:43
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by TronDoc.

I get an error on this line:
call *gpPicture\Release
PureBASIC error message:
Line 69 This variable doesn't have a 'Structure'


elecTRONics DOCtor
{registeredPB}P150 32Mb w98/DOS/Linux NO DirX NO IE :wink:
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

I get the same compile error. It's odd, since the definition of *gpPicture is given as:

Global *gpPicture.IPictureMethods

and IPictureMethods is a stucture.

Eric

I get an error on this line:
call *gpPicture\Release
PureBASIC error message:
Line 69 This variable doesn't have a 'Structure'


elecTRONics DOCtor
{registeredPB}P150 32Mb w98/DOS/Linux NO DirX NO IE :wink:
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by fred.

CALL is an ASM function. Enable the Inline ASM in the compiler options.

Fred - AlphaSND
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by TronDoc.

OOPS! Thanks Fred. I think I made that mistake once before.

elecTRONics DOCtor
{registeredPB}P150 32Mb w98/DOS/Linux NO DirX NO IE :wink:

EDIT:
ooow. now it's worse! I get Purebasic36523192 error:
This program has performed an illegal operation and will be shut down.
If the problem persists, contact the program vendor.
{DETAILS:PUREBASIC36523192 caused a stack fault in module KERNEL32.DLL at 015f:bff742a3.}
And the PureBASIC - Debugger windows says Error at line 226:
InitKeyboard() must be called before use the keyboard commands.
I see that InitKeyboard() is a DirectX command. Oh well.

Edited by - TronDoc on 30 June 2002 21:13:27

Edited by - TronDoc on 30 June 2002 21:15:34
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

TronDoc,

When I enabled Inline ASM as Fred said, the program compiled and ran fine for me (Win XP Pro).

Eric

OOPS! Thanks Fred. I think I made that mistake once before.

elecTRONics DOCtor
{registeredPB}P150 32Mb w98/DOS/Linux NO DirX NO IE :wink:

EDIT:
ooow. now it's worse! I get Purebasic36523192 error:
This program has performed an illegal operation and will be shut down.
If the problem persists, contact the program vendor.
{DETAILS:PUREBASIC36523192 caused a stack fault in module KERNEL32.DLL at 015f:bff742a3.}
And the PureBASIC - Debugger windows says Error at line 226:
InitKeyboard() must be called before use the keyboard commands.
I see that InitKeyboard() is a DirectX command. Oh well.

Edited by - TronDoc on 30 June 2002 21:13:27

Edited by - TronDoc on 30 June 2002 21:15:34
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

El_Choni,

Thanks for the JPG display code. Since I don't have a prayer of understanding it completely, I have a question. When I load an image, if I move the window so part of it is off-screen (or move another window in front of it), the covered part of the image disappears. Is there any way to make sure the image remains displayed?

Thanks again,
Eric
Hi,

This is just for etsting purposes, not a complete app, and you'll notice there are lots of features missing; but it loads and shows jpeg and gif files. Enjoy it;




Bye,

El_Choni
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by Danilo.

Repaint the image after a #PB_EventRepaint or
#WM_PAINT (in callback) message.

cya,
...Danilo

(registered PureBasic user)
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by PB.

> When I enabled Inline ASM as Fred said, the program compiled and ran fine for me (Win XP Pro).

Fred: Is it okay to leave Inline ASM turned on for all apps, even if they don't
use any assembly code? Does it have any side-effects? Seems to me that it would
solve a lot of these "can't compile" problems...?

PB - Registered PureBasic Coder
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

Anyway, the updated code doesn't use DX (but it must use Asm to store the CLSID).

Bye,

El_Choni
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

El_Choni,

Thanks for all your hard work! The new code works much better on PB 3.2. I will try to incorporate it into my application, if that is OK with you. How big do you want your name on the scrolling credit screen???

Fred,

Is built-in JPG support in a future version of PB likely?

Thanks,
Eric

Anyway, the updated code doesn't use DX (but it must use Asm to store the CLSID).

Bye,

El_Choni
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

El_Choni,

As usual(!), I have a question about your JPG display code. I noticed that when an image is displayed, the program uses over 90% of the CPU time. I believe this is because of the call to "UpdateImage()" whenever a #WM_PAINT message is received in the "ResizeCallback()" routine. It seems that #WM_PAINT messages are constantly coming in, even when there is no activity on the window - no mouse movement, no other windows moving, just idle. This doesn't make sense to me; I thought a window was repainted only when necessary. In any case, do you have any suggestions as to how to eliminate the unnecessary repaints and decrease the CPU time when no activity is occurring?

BTW - I really like your file drag and drop code! I didn't realize it was that simple. Of course, EVERYTHING is easy when you know how!

Thanks,
Eric
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by El_Choni.

Hi,

You're right about the CPU problem, but repainting must be done at some moment, and I don't know how to solve this, sorry.

Of course, you can use any part of the code or the whole of it. No need for acknowledgements, almost all is made from copy/paste from Internet.

Feel free to ask for any help. Have a nice day,

El_Choni
BackupUser
PureBasic Guru
PureBasic Guru
Posts: 16777133
Joined: Tue Apr 22, 2003 7:42 pm

Post by BackupUser »

Restored from previous forum. Originally posted by ebs.

El_Choni,

I don't think the problem is redrawing the JPG image when a #WM_PAINT message is received - that's the right time! The problem seems to be that the window CONSTANTLY receives #WM_PAINT messages, even when nothing is happening on the screen. A standard window with an ImageGadget gets #WM_PAINT messages only when part of the window has been covered (by another window, moving offscreen, etc.). Do you have any idea why this happens when using the IPicture COM object?

Fred,

Please consider extending PB's ImageGadget to support reading and displaying (maybe even saving) JPG images. It would be GREAT to have all the ImageGadget features (automatic resizing, etc.) and be able to use JPG images.
How 'bout it?

Regards,
Eric
Hi,

You're right about the CPU problem, but repainting must be done at some moment, and I don't know how to solve this, sorry.

Of course, you can use any part of the code or the whole of it. No need for acknowledgements, almost all is made from copy/paste from Internet.

Feel free to ask for any help. Have a nice day,

El_Choni
Post Reply