Page 1 of 1

How do I display Multipage tiff image

Posted: Thu Mar 27, 2014 1:56 pm
by leodh
Hi,

I am trying to find a way to display Multipage Tiff images in PB.

If I use the Tiff decoder it only displays the first page, is there a way to display them with PB or do I have to use a library to display them. I would be happy with the pages to be joined at the bottom of the previous page.

Does anyone have any ideas or clues.

Thanks

Re: How do I display Multipage tiff image

Posted: Thu Mar 27, 2014 7:11 pm
by netmaestro
Your best bet would probably be the FreeImage library. Gdiplus is capable as well but with more effort required from the coder. You could always write your own decoder like Wilbert and I both did for Gif over the last 2 months of 2013 but that would be overkill unless you really love tiffs.

Re: How do I display Multipage tiff image

Posted: Thu Mar 27, 2014 11:19 pm
by coco2
I always liked multipage tiffs for document storage although I now scan and OCR documents into PDF so they are searchable so I stopped using TIFF. Would be handy to have a native PB reader.

Re: How do I display Multipage tiff image

Posted: Fri Mar 28, 2014 8:05 am
by leodh
Hi,

I got this far but can't display the other pages.

Code: Select all

; Modified from ************************************
;==================================================
;  Program:     GDIPlus Rotation Demo
;  Author:      netmaestro
;  Date:        November 12, 2006
;==================================================
;****************************************************

CompilerIf Defined(GdiplusStartupInput, #PB_Structure) = 0
  Structure GdiplusStartupInput
    GdiPlusVersion.l
    *DebugCallback
    SuppressBackgroundThread.l
    SuppressExternalCodecs.l
  EndStructure
CompilerEndIf 

Prototype GdiplusStartup( *token, *input, mode )
Prototype GdipCreateBitmapFromFile( Filename, *image )
Prototype GdipGetImageWidth( *image, *Width )
Prototype GdipGetImageHeight( *image, *Height )
Prototype GdipCreateFromHDC( hdc, *gfx)
Prototype GdipDrawImageRectI( *gfx, *image, x, y, Width, Height )
Prototype GdipDeleteGraphics( *gfx )
Prototype GdipDisposeImage( *image )
Prototype GdiplusShutdown( *token )

OpenLibrary(0, "gdiplus.dll")

Global Startup.GdiplusStartup                        = GetFunction( 0, "GdiplusStartup" )         
Global CreateBitmapFromFile.GdipCreateBitmapFromFile = GetFunction( 0, "GdipCreateBitmapFromFile" )
Global GetImageWidth.GdipGetImageWidth               = GetFunction( 0, "GdipGetImageWidth" )               
Global GetImageHeight.GdipGetImageHeight             = GetFunction( 0, "GdipGetImageHeight" )     
Global CreateFromHDC.GdipCreateFromHDC               = GetFunction( 0, "GdipCreateFromHDC" )     
Global DrawImageRectI.GdipDrawImageRectI             = GetFunction( 0, "GdipDrawImageRectI" )     
Global DeleteGraphics.GdipDeleteGraphics             = GetFunction( 0, "GdipDeleteGraphics" )     
Global DisposeImage.GdipDisposeImage                 = GetFunction( 0, "GdipDisposeImage" )       
Global Shutdown.GdiplusShutdown                      = GetFunction( 0, "GdiplusShutdown" ) 
;     
;-----------------------------------------------------------------------------
;                      End GDIPlus Initialization Section
;-----------------------------------------------------------------------------

Procedure StringToBStr (string$) ; By Zapman Inspired by Fr34k
  Protected Unicode$ = Space(Len(string$)* 2 + 2)
  Protected bstr_string.l
  PokeS(@Unicode$, string$, -1, #PB_Unicode)
  bstr_string = SysAllocString_(@Unicode$)
  ProcedureReturn bstr_string
EndProcedure

input.GdiplusStartupInput
input\GdiPlusVersion = 1
Startup( @*token, @input, #Null)

FileName.s = "C:\AS-13-0731-RS-1.tif"
Imagenumber = 0
CreateBitmapFromFile( StringToBStr(Filename), @*image)
GetImageWidth( *image, @Width.l )
GetImageHeight( *image, @Height.l )
Retval = CreateImage(ImageNumber, width, Height, 24)
hdc = StartDrawing(ImageOutput(ImageNumber))
    DrawingMode(#PB_2DDrawing_AlphaChannel)
    Box(0, 0, width, height, 0)
  CreateFromHDC( hdc, @*gfx )
  DrawImageRectI( *gfx, *image, 0, 0, Width, Height)
StopDrawing() 
DeleteGraphics( *gfx ) 
DisposeImage( *image )
Shutdown( *token )
  
CreateImage(1, width, height, 24)
StartDrawing(ImageOutput(1))
  Box(0, 0, width, Height, GetSysColor_(#COLOR_3DFACE))
  DrawAlphaImage(ImageID(0), 0, 0)
StopDrawing()

CloseLibrary(0)

OpenWindow(0,0,0,600,800,"")
ScrollAreaGadget(99,0,0,600,800,1700,2200)
ImageGadget(0,0,0,Width,Height,ImageID(1))
CloseGadgetList()
Repeat
  
Until WaitWindowEvent() = #WM_CLOSE
ShutDownGDIPlus(*token)
Does anyone have an example of multipage display.

Regards

Re: How do I display Multipage tiff image

Posted: Fri Mar 28, 2014 8:17 am
by infratec
Hi,

look at
http://www.purebasic.fr/english/viewtop ... hilit=tiff

at the bottom of the first page.

Bernd

Re: How do I display Multipage tiff image

Posted: Fri Mar 28, 2014 9:35 am
by wilbert
I think it should be possible with the decoder that comes with PureBasic.
If you load the tiff file into memory, you should be able to change the offset of the image data to the next page, catch the image and move on to the next one.

Re: How do I display Multipage tiff image

Posted: Fri Mar 28, 2014 11:22 am
by wilbert
I found some test files and can confirm it's easy to create a simple cross platform solution. :D

Code: Select all

UseTIFFImageDecoder()

Procedure.w SwapBytesW(v.w)
  !movzx eax, word [p.v_v]
  !xchg al, ah
  ProcedureReturn
EndProcedure

Procedure.l SwapBytesL(v.l)
  !mov eax, [p.v_v]
  !bswap eax
  ProcedureReturn
EndProcedure

Procedure.i CatchTIFFPage(Image, *MemoryAddress.Word, Page = 0)
  Protected.i Result, Count, *Entries.Word
  Protected *IFD0.Long = *MemoryAddress + 4
  Protected *IFD.Long = *IFD0
  While *IFD\l
    If Count = Page
      Swap *IFD0\l, *IFD\l
      Result = CatchImage(Image, *MemoryAddress)
      Swap *IFD0\l, *IFD\l
      Break
    EndIf
    Count + 1  
    If *MemoryAddress\w = $4D4D
      ; big endian
      *Entries = *MemoryAddress + SwapBytesL(*IFD\l)
      *IFD = *Entries + 2 + SwapBytesW(*Entries\w) * 12
    Else
      ; little endian
      *Entries = *MemoryAddress + *IFD\l
      *IFD = *Entries + 2 + *Entries\w * 12
    EndIf
  Wend
  If Page >= 0
    ProcedureReturn Result
  Else
    ProcedureReturn Count
  EndIf
EndProcedure

Code: Select all

CatchTIFFPage(0, *TIFFData, 1); Catches the second page (page number starts at 0)
If you specify a negative page number, the procedure returns the amount of pages.

Re: How do I display Multipage tiff image

Posted: Sun Mar 30, 2014 3:40 pm
by leodh
Hi Wilbert,

I have had a look at your code and I think I understand what you are saying, but I can not get it to work, I've not done much with images.

When you say load the Tiff file into memory, how do I load it into memory as Image 0 ?

In the statement CatchTiffPage is *TIFFData the pointer to image was loaded ? I think I tried just about everything I could think of.

I am most probably doing something really silly, but I have no idea.

Code: Select all


OpenWindow(0,0,0,800,700,"",#PB_Window_ScreenCentered)

CreateImage(0,800,600,24)
LoadImage(0,FileName.s)

ImageGadget(0,0,0,800,600,ImageID(0))
Delay(1000)

For t = 1 To 9
  CatchTIFFPage(0, *TIFFData, t); Catches the t page (10 pages in Image)
  SetGadgetState(0,ImageID(0))
Next t

Repeat
  
Until WaitWindowEvent() = #WM_CLOSE
Could you push me in the right direction.

Thanks

Re: How do I display Multipage tiff image

Posted: Sun Mar 30, 2014 4:48 pm
by wilbert
leodh wrote:When you say load the Tiff file into memory, how do I load it into memory as Image 0 ?
Here's a more complete example.
Hopefully this helps.

Code: Select all

UseTIFFImageDecoder()

Procedure.w SwapBytesW(v.w)
  !movzx eax, word [p.v_v]
  !xchg al, ah
  ProcedureReturn
EndProcedure

Procedure.l SwapBytesL(v.l)
  !mov eax, [p.v_v]
  !bswap eax
  ProcedureReturn
EndProcedure

Procedure.i CatchTIFFPage(Image, *MemoryAddress.Word, Page = 0)
  Protected.i Result, Count, *Entries.Word
  Protected *IFD0.Long = *MemoryAddress + 4
  Protected *IFD.Long = *IFD0
  While *IFD\l
    If Count = Page
      Swap *IFD0\l, *IFD\l
      Result = CatchImage(Image, *MemoryAddress)
      Swap *IFD0\l, *IFD\l
      Break
    EndIf
    Count + 1  
    If *MemoryAddress\w = $4D4D
      ; big endian
      *Entries = *MemoryAddress + SwapBytesL(*IFD\l)
      *IFD = *Entries + 2 + SwapBytesW(*Entries\w) * 12
    Else
      ; little endian
      *Entries = *MemoryAddress + *IFD\l
      *IFD = *Entries + 2 + *Entries\w * 12
    EndIf
  Wend
  If Page >= 0
    ProcedureReturn Result
  Else
    ProcedureReturn Count
  EndIf
EndProcedure

Procedure.i LoadFile(FileName.s)
  Protected.i File, Length, *Memory
  File = ReadFile(#PB_Any, FileName)
  If File
    Length = Lof(File)
    *Memory = AllocateMemory(Length)
    If *Memory
      ReadData(File, *Memory, Length)
    EndIf
    CloseFile(File)
  EndIf
  ProcedureReturn *Memory
EndProcedure



; *** Test code ***

Define.i Event, Page, PageCount, *TIFFData

OpenWindow(0, 0, 0, 800, 700, "", #PB_Window_ScreenCentered)

*TIFFData = LoadFile("MyTiffFile.tiff")
If *TIFFData
  PageCount = CatchTIFFPage(0, *TIFFData, -1)
  
  For Page = 0 To PageCount - 1
    CatchTIFFPage(Page, *TIFFData, Page)
  Next
  
  FreeMemory(*TIFFData)
  
  ImageGadget(0, 0, 0, 800, 600, ImageID(0))
  
  Repeat
    Event = WaitWindowEvent()
    
    If Event = #PB_Event_Gadget And EventType() = #PB_EventType_LeftClick
      Page = (Page + 1) % PageCount
      SetGadgetState(0, ImageID(Page))
    EndIf
    
  Until Event = #PB_Event_CloseWindow
  
EndIf
The LoadFile procedure loads the tiff file into memory.
After that you can catch the images you want.
When the tiff data is no longer needed, FreeMemory needs to be used to free the memory it occupies.

Re: How do I display Multipage tiff image

Posted: Mon Mar 31, 2014 12:17 am
by leodh
Hi Wilbert,

Thank you, I was close but I could not figure out the memory allocation stuff.

Once again the PB community helps a battler.

Thanks again.