Custom image plugin

Mac OSX specific forum
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Custom image plugin

Post by wilbert »

See this thread for the most recent cross platform version
http://www.purebasic.fr/english/viewtop ... 12&t=66497




Cross platform ImagePlugin.pbi file

Code: Select all

; *** ImagePlugin.pbi ***

; MacOS, Linux  : Use ProcedureC for Check, Decode, Cleanup, Encode24 and Encode32
;                 Use PeekS with UTF8 encoding to get a string from *Filename
; Windows       : Use Procedure for Check, Decode, Cleanup, Encode24 and Encode32
;                 Use PeekS with current encoding to get a string from *Filename

; ReverseY can be checked with Flags argument of Decode, Encode24 and Encode32

#PB_ImageDecoder_File     = 0
#PB_ImageDecoder_Memory   = 1
#PB_ImageDecoder_ReverseY = 2

Structure PB_ImageDecoder Align #PB_Structure_AlignC
  *Check
  *Decode
  *Cleanup
  ID.l
EndStructure

Structure PB_ImageDecoderGlobals Align #PB_Structure_AlignC
  *Decoder.PB_ImageDecoder
  *Filename
  *File
  *Buffer
  Length.l
  Mode.l
  Width.l
  Height.l
  Depth.l
  Flags.l
  Data.i[8]
  OriginalDepth.l
EndStructure

Structure PB_ImageEncoder Align #PB_Structure_AlignC
  ID.l
  *Encode24
  *Encode32
EndStructure

IsImage(0); Make sure PB Image functionality is included

CompilerIf #PB_Compiler_OS = #PB_OS_Windows And #PB_Compiler_Processor = #PB_Processor_x86  
  Import ""
    PB_ImageDecoder_Register(*ImageDecoder.PB_ImageDecoder) As "_PB_ImageDecoder_Register@4"
    PB_ImageEncoder_Register(*ImageEncoder.PB_ImageEncoder) As "_PB_ImageEncoder_Register@4"
  EndImport
CompilerElse
  ImportC ""
    PB_ImageDecoder_Register(*ImageDecoder.PB_ImageDecoder)
    PB_ImageEncoder_Register(*ImageEncoder.PB_ImageEncoder)
  EndImport
CompilerEndIf
Plugins to be used with this include file :

OSX
GIF Decode / Encode
Decode all Mac supported types
Last edited by wilbert on Mon Sep 05, 2016 11:37 am, edited 8 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Custom image type plugin

Post by wilbert »

#PB_ImagePlugin_GIF for OSX.

Added functionality :
UseGIFImageDecoder()
UseGIFImageEncoder()


Remarks :
Currently original image depth is always set to 8 bits.
SaveImage / EncodeImage unfortunately ignore setting for requested bit depth (I couldn't find a way to tell OSX to write 1 bit or 4 bit images).

Code: Select all

; *** #PB_ImagePlugin_GIF ***

#PB_ImagePlugin_GIF = $464947

XIncludeFile "ImagePlugin.pbi"

ImportC ""
  CFDataCreate(allocator, *bytes, length)
  CFRelease(cf)
  CGBitmapContextCreate(*data, width, height, bitsPerComponent, bytesPerRow, colorspace, bitmapInfo)
  CGColorSpaceCreateDeviceRGB()
  CGColorSpaceRelease(colorspace)
  CGContextDrawImage(c, xf.f, yf.f, wf.f, hf.f, image, d0.f, d1.f, d2.f, d3.f, xd.d, yd.d, wd.d, hd.d)
  CGContextRelease(context)
  CGDataProviderCreateWithCFData(cfdata)
  CGDataProviderCreateWithData(*info, *data, size, releaseData)
  CGDataProviderRelease(provider)
  CGImageCreate(width, height, bitsPerComponent, bitsPerPixel, bytesPerRow, space, bitmapInfo, provider, *decode, shouldInterpolate, intent)
  CGImageGetBitsPerPixel(image)
  CGImageRelease(image)
  CGImageSourceCreateImageAtIndex(isrc, index, options)
  CGImageSourceCreateWithDataProvider(provider, options)
EndImport


; *** Decoder procedures ***

ProcedureC   _GIFCleanup_(*Globals.PB_ImageDecoderGlobals)
  If *Globals\Mode = #PB_ImageDecoder_File And *Globals\Buffer
    FreeMemory(*Globals\Buffer) : *Globals\Buffer = #Null : *Globals\Length = 0
  EndIf
EndProcedure

ProcedureC.i _GIFCheck_(*Globals.PB_ImageDecoderGlobals)
  
  If *Globals\Mode = #PB_ImageDecoder_File
    ; File Mode
    Protected.i GIFFile = ReadFile(#PB_Any, PeekS(*Globals\Filename, -1, #PB_UTF8))
    If GIFFile And Lof(GIFFile) >= 12
      *Globals\Length = Lof(GIFFile)
      *Globals\Buffer = AllocateMemory(*Globals\Length, #PB_Memory_NoClear)
      If *Globals\Buffer
        ReadData(GIFFile, *Globals\Buffer, 12)
        If PeekL(*Globals\Buffer) = $38464947 ; 'GIF8'
          *Globals\Width  = PeekU(*Globals\Buffer + 6)
          *Globals\Height = PeekU(*Globals\Buffer + 8)
          If *Globals\Width And *Globals\Height
            ReadData(GIFFile, *Globals\Buffer + 12, *Globals\Length - 12)
          Else
            _GIFCleanup_(*Globals)
          EndIf
        EndIf
      EndIf
      CloseFile(GIFFile)
    EndIf
  ElseIf *Globals\Length >= 12
    ; Memory mode
    If PeekL(*Globals\Buffer) = $38464947 ; 'GIF8'
      *Globals\Width  = PeekU(*Globals\Buffer + 6)
      *Globals\Height = PeekU(*Globals\Buffer + 8)
    EndIf
  EndIf
  
  If *Globals\Width And *Globals\Height
    *Globals\Depth = 32
    *Globals\OriginalDepth = 8
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
   
EndProcedure

ProcedureC.i _GIFDecode_(*Globals.PB_ImageDecoderGlobals, *Buffer, Pitch, Flags)
  
  Protected.i ColorSpace, Context, Image, Provider, Source
  
  Provider = CGDataProviderCreateWithData(#Null, *Globals\Buffer, *Globals\Length, #Null)
  Source = CGImageSourceCreateWithDataProvider(Provider, #Null)
  Image = CGImageSourceCreateImageAtIndex(Source, 0, #Null)
  ColorSpace = CGColorSpaceCreateDeviceRGB()
  Context = CGBitmapContextCreate(*Buffer, *Globals\Width, *Globals\Height, 8, Pitch, ColorSpace, 1)
  CGContextDrawImage(Context, 0, 0, *Globals\Width, *Globals\Height, image, 0,0,0,0, 0, 0, *Globals\Width, *Globals\Height)
  CGContextRelease(Context)
  CGColorSpaceRelease(ColorSpace)
  CGImageRelease(Image)
  CFRelease(Source)
  CGDataProviderRelease(Provider)
  
  _GIFCleanup_(*Globals)
  ProcedureReturn #True
  
EndProcedure

Procedure UseGIFImageDecoder()
  Static GIFDecoder.PB_ImageDecoder, Registered
  If Not Registered
    GIFDecoder\ID = #PB_ImagePlugin_GIF
    GIFDecoder\Check    = @_GIFCheck_()
    GIFDecoder\Cleanup  = @_GIFCleanup_()
    GIFDecoder\Decode   = @_GIFDecode_()
    PB_ImageDecoder_Register(GIFDecoder)
    Registered = #True
  EndIf
  ProcedureReturn Registered
EndProcedure


; *** Encoder procedures ***

ProcedureC.i _GIFEncode_(*Filename, *Buffer, Width, Height, LinePitch, Flags, EncoderFlags, RequestedDepth)
  
  Protected.i CFData, ColorSpace, Image, ImageData, ImageRep, Length, Pool, Provider, Result
  Protected.s FileName
  
  ; Convert to NSBitmapImageRep
  CFData = CFDataCreate(#Null, *Buffer, Height * LinePitch)
  Provider = CGDataProviderCreateWithCFData(CFData)
  ColorSpace = CGColorSpaceCreateDeviceRGB()
  If RequestedDepth = 32
    Image = CGImageCreate(Width, Height, 8, 32, LinePitch, ColorSpace, 1, Provider, #Null, #False, 0)
  Else
    Image = CGImageCreate(Width, Height, 8, 24, LinePitch, ColorSpace, 0, Provider, #Null, #False, 0)
  EndIf
  ImageRep = CocoaMessage(0, CocoaMessage(0, 0, "NSBitmapImageRep alloc"), "initWithCGImage:", Image)
  CGImageRelease(Image)
  CGColorSpaceRelease(ColorSpace)
  CGDataProviderRelease(Provider)
  CFRelease(CFData)
  
  ; Convert NSBitmapImageRep to GIF
  If ImageRep
    Pool = CocoaMessage(0, 0, "NSAutoreleasePool new")
    ImageData = CocoaMessage(0, ImageRep, "representationUsingType:", 2, "properties:", #Null)
    If *Filename
      ; File Mode
      FileName = PeekS(*Filename, -1, #PB_UTF8)
      Result = CocoaMessage(0, ImageData, "writeToFile:$", @FileName, "atomically:", #NO)
    Else
      ; Memory Mode
      Length = CocoaMessage(0, ImageData, "length")
      Result = AllocateMemory(Length, #PB_Memory_NoClear)
      CocoaMessage(0, ImageData, "getBytes:", Result, "length:", Length)
    EndIf
    CocoaMessage(0, Pool, "release")
    CocoaMessage(0, ImageRep, "release")
  EndIf
  
  ProcedureReturn Result
  
EndProcedure

Procedure UseGIFImageEncoder()
  Static GIFEncoder.PB_ImageEncoder, Registered
  If Not Registered
    GIFEncoder\ID = #PB_ImagePlugin_GIF
    GIFEncoder\Encode24 = @_GIFEncode_()
    GIFEncoder\Encode32 = @_GIFEncode_()
    PB_ImageEncoder_Register(GIFEncoder)
    Registered = #True
  EndIf
  ProcedureReturn Registered
EndProcedure
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Custom image type plugin (GIF)

Post by J. Baker »

Very nice wilbert! Thanks!

I only tried out the decoder so far. Will try the encoder in just a bit. :D
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Custom image type plugin (GIF)

Post by J. Baker »

wilbert wrote:Remarks :
Currently original image depth is always set to 8 bits.
SaveImage / EncodeImage unfortunately ignore setting for requested bit depth (I couldn't find a way to tell OSX to write 1 bit or 4 bit images).
The encoder works nice! While you may have not found a way for 1 or 4 bit, 8 bit is better than nothing at all. :D
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Custom image type plugin (GIF)

Post by wilbert »

J. Baker wrote:The encoder works nice! While you may have not found a way for 1 or 4 bit, 8 bit is better than nothing at all. :D
You are right. Thanks :D

When it comes to decoding, it's also possible to create a single plugin that adds support for all bitmap image formats OSX supports.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Custom image type plugin (GIF)

Post by J. Baker »

wilbert wrote: You are right. Thanks :D

When it comes to decoding, it's also possible to create a single plugin that adds support for all bitmap image formats OSX supports.
Very nice! :D

If it were to encode an animated gif, would you write all frame data and so forth to memory, then use SaveImage()? I just didn't know if SaveImage() could still be used for that method or not?

EDIT: I just answered my own question with a bit of code. It will export static. Unless CopyImage() destroys it?
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Custom image type plugin (GIF)

Post by wilbert »

J. Baker wrote:If it were to encode an animated gif, would you write all frame data and so forth to memory, then use SaveImage()? I just didn't know if SaveImage() could still be used for that method or not?
You were right. PureBasic doesn't support multiframe images so that won't work.


Here's a plugin that adds all Mac supported types like gif, tiff, jpg, png, psd .
UseMacImageDecoder()

Code: Select all

; *** MacImageDecoder ***

XIncludeFile "ImagePlugin.pbi"

CompilerIf Not Defined(vImage_Buffer, #PB_Structure)
  Structure vImage_Buffer
    *data
    height.i
    width.i
    rowBytes.i
  EndStructure
CompilerEndIf

ImportC "-framework Accelerate"
  strlen(*s)
  vImageUnpremultiplyData_RGBA8888(*src, *dest, flags) 
  CFRelease(cf)
  CFURLCreateFromFileSystemRepresentation(allocator, *buffer, bufLen, isDirectory)
  CGBitmapContextCreate(*data, width, height, bitsPerComponent, bytesPerRow, colorspace, bitmapInfo)
  CGColorSpaceCreateDeviceRGB()
  CGColorSpaceRelease(colorspace)
  CGContextDrawImage(c, xf.f, yf.f, wf.f, hf.f, image, d0.f, d1.f, d2.f, d3.f, xd.d, yd.d, wd.d, hd.d)
  CGContextRelease(context)
  CGDataProviderCreateWithData(*info, *data, size, releaseData)
  CGDataProviderRelease(provider)
  CGImageGetAlphaInfo(image)
  CGImageGetBitsPerPixel(image)
  CGImageGetHeight(image)
  CGImageGetWidth(image)
  CGImageRelease(image)
  CGImageSourceCreateImageAtIndex(isrc, index, options)
  CGImageSourceCreateWithDataProvider(provider, options)
  CGImageSourceCreateWithURL(url, options)
EndImport


; *** Decoder procedures ***

ProcedureC   _MacCleanup_(*Globals.PB_ImageDecoderGlobals)
  If *Globals\Data[0]
    CGImageRelease(*Globals\Data[0]) : *Globals\Data[0] = #Null
  EndIf
EndProcedure

ProcedureC.i _MacCheck_(*Globals.PB_ImageDecoderGlobals)
  
  Protected.i Image, ImageSource, Properties, Provider, URL
  
  If *Globals\Mode = #PB_ImageDecoder_File
    ; File Mode
    URL = CFURLCreateFromFileSystemRepresentation(#Null, *Globals\Filename, strlen(*Globals\Filename), #False)
    ImageSource = CGImageSourceCreateWithURL(URL, #Null)
    CFRelease(URL)
  Else
    ; Memory Mode
    Provider = CGDataProviderCreateWithData(#Null, *Globals\Buffer, *Globals\Length, #Null)
    ImageSource = CGImageSourceCreateWithDataProvider(Provider, #Null)
    CGDataProviderRelease(Provider)
  EndIf
  
  If ImageSource
    Image = CGImageSourceCreateImageAtIndex(ImageSource, 0, #Null)
    CFRelease(ImageSource)
    If Image
      *Globals\Data[0] = Image
      *Globals\Data[1] = CGImageGetAlphaInfo(Image)
      *Globals\Width = CGImageGetWidth(Image)
      *Globals\Height = CGImageGetHeight(Image)
      *Globals\OriginalDepth = CGImageGetBitsPerPixel(Image)
      *Globals\Depth = 32
      ProcedureReturn #True
    EndIf
  EndIf
  ProcedureReturn #False
   
EndProcedure

ProcedureC.i _MacDecode_(*Globals.PB_ImageDecoderGlobals, *Buffer, Pitch, Flags)
  
  Protected.i ColorSpace, Context, vImg.vImage_Buffer

  ColorSpace = CGColorSpaceCreateDeviceRGB()
  Context = CGBitmapContextCreate(*Buffer, *Globals\Width, *Globals\Height, 8, Pitch, ColorSpace, 1)
  CGContextDrawImage(Context, 0, 0, *Globals\Width, *Globals\Height, *Globals\Data[0], 0,0,0,0, 0, 0, *Globals\Width, *Globals\Height)
  CGContextRelease(Context)
  CGColorSpaceRelease(ColorSpace)
  
  If *Globals\Data[1] And *Globals\Data[1] < 5; Alpha channel check
    vImg\data = *Buffer
    vImg\width = *Globals\Width
    vImg\height = *Globals\Height
    vImg\rowBytes = Pitch
    vImageUnPremultiplyData_RGBA8888(@vImg, @vImg, 0)
  EndIf
 
  _MacCleanup_(*Globals)
  ProcedureReturn #True
  
EndProcedure

Procedure UseMacImageDecoder()
  Static MacDecoder.PB_ImageDecoder, Registered
  If Not Registered
    MacDecoder\ID       = $63614D; 'Mac'
    MacDecoder\Check    = @_MacCheck_()
    MacDecoder\Cleanup  = @_MacCleanup_()
    MacDecoder\Decode   = @_MacDecode_()
    PB_ImageDecoder_Register(MacDecoder)
    Registered = #True
  EndIf
  ProcedureReturn Registered
EndProcedure
Last edited by wilbert on Thu Mar 25, 2021 3:01 pm, edited 1 time in total.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Custom image type plugin (GIF)

Post by J. Baker »

Sweet! One plugin to decode them all! :lol:

Very nice wilbert! ;)
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
User avatar
J. Baker
Addict
Addict
Posts: 2178
Joined: Sun Apr 27, 2003 8:12 am
Location: USA
Contact:

Re: Custom image type plugin (GIF)

Post by J. Baker »

Remember that code you sent me some time back, where you drew out all the frames of a gif on a single image? Check this out. I modified it a bit. :D

EDIT: I think you made a GIF Explorer though. I have to look through my code folder.

Code: Select all

AnimGifName.s = GetHomeDirectory() + "Desktop/er14.gif"

Image = CocoaMessage(0, CocoaMessage(0, 0, "NSImage alloc"), "initWithContentsOfFile:$", @AnimGifName)

If Image ;if gif is loaded, draw its frames
  
     ImageRep = CocoaMessage(0, CocoaMessage(0, Image, "representations"), "objectAtIndex:", 0)
     FrameCount = CocoaMessage(0, CocoaMessage(0, ImageRep, "valueForProperty:$", @"NSImageFrameCount"), "intValue")
     CocoaMessage(@ImageSize.NSSize, Image, "size")
     
      GifFrames = 0
     While GifFrames < FrameCount
       CreateImage(GifFrames, ImageSize\width, ImageSize\height, 32, #PB_Image_Transparent)
         StartDrawing(ImageOutput(GifFrames))
         DrawingMode(#PB_2DDrawing_AllChannels)
           CocoaMessage(0, ImageRep, "setProperty:$", @"NSImageCurrentFrame", "withValue:", CocoaMessage(0, 0, "NSNumber numberWithInt:", GifFrames))
           DrawImage(Image, 0, 0)
         StopDrawing()
      GifFrames + 1
     Wend
     
     CocoaMessage(0, Image, "release")
     
EndIf

If OpenWindow(0, 0, 0, ImageWidth(0) + 20, ImageHeight(0) + 20, "GIF", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
     ImageGadget(0, 10, 10, ImageWidth(0), ImageHeight(0), ImageID(0))
     GF = 0 ;gif frame starts at 0
     Quit = 0
     StartTime = ElapsedMilliseconds()
  Repeat
    
    Event = WindowEvent()
    
    If Event = #PB_Event_CloseWindow
      Quit = 1
    EndIf
    
    If ElapsedMilliseconds() - StartTime >= 33 ;gif delay/ms
       SetGadgetState(0, ImageID(GF))
         If GF < (FrameCount -1)
           GF + 1
         Else
           GF = 0
         EndIf
       StartTime = ElapsedMilliseconds()
    EndIf
    
    Delay(1) ;cpu saver
    
  Until Quit = 1
EndIf
www.posemotion.com

PureBasic Tools for OS X: PureMonitor, plist Tool, Data Maker & App Chef

Mac: 10.13.6 / 1.4GHz Core 2 Duo / 2GB DDR3 / Nvidia 320M
PC: Win 7 / AMD 64 4000+ / 3GB DDR / Nvidia 720GT


Even the vine knows it surroundings but the man with eyes does not.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Custom image type plugin (GIF)

Post by wilbert »

J. Baker wrote:I think you made a GIF Explorer though. I have to look through my code folder.
I did but it's good to see another approach. Your code is working fine :)
Windows (x64)
Raspberry Pi OS (Arm64)
Post Reply