
# ImgConv.py

"""
Based on pyWiki 'Working With Images' @  http://wiki.wxpython.org/index.cgi/WorkingWithImages
Modified to properly copy, create or remove any alpha in all conversion permutations.

Note:  The terms "plane", "band", "layer" and "channel" are used interchangibly.

Tested on Win7 64-bit (6.1.7600) and Win XP SP3 (5.1.2600) using Python 32-bit.

Platform  Windows 6.1.7600
Python    2.5.4 (r254:67916, Dec 23 2008, 15:10:54) [MSC v.1310 32 bit (Intel) (x86)]
Python wx 2.8.10.1
Pil       1.1.7

Ray Pasco      
pascor(at)verizon(dot)net

Last modification:      2011-04-02

This code may be modified and distributed for any purpose whatsoever.

"""

import sys

import wx       # WxBmap <==> wxImage
import Image    # wxImage <==> PilImage.  Used whenf converting to or from Pil images.

#------------------------------------------------------------------------------

#   image type         image type       image type
#       1                  2                3
#   wx.Bitmap   <==>   wx.Image  <==>   pilImage

#----------------------------

def WxBitmapFromPilImage( pilImage, addAlphaLayer=False, delAlphaLayer=False ) :
    """
    3 ==> 1
    
    The default parameter valuess preserve any existing transparency, 
      but do not add one if none exists.
    
    If the given pilImage mode is RGB, optionally create a new wxImage 
      alpha transparency plane by calling with addAlphaLayer=True.
      
    If the given pilImage mode is RGBA, optionally create a new wxImage 
      and delete any transparency plane by calling with delAlphaLayer=True.
      
    If given *both* addAlphaLayer=True *and* delAlphaLayer=True, 
      addAlphaLayer=True will take precidence.
      
    """
    
    # Pass on the given parameters.
    wxImage = WxImageFromPilImage( pilImage, addAlphaLayer, delAlphaLayer )
    wxBitmap = wxImage.ConvertToBitmap()
    
    return wxBitmap
#end def

#----------------------------

def WxImageFromPilImage( pilImage, addAlphaLayer=False, delAlphaLayer=False ) :
    """
    3 ==> 2
    
    The default parameter values preserve any existing transparency, 
      but do not add one if it none exists in the source image.
    
    If the given source has no alpha, a new image with alpha transparency 
      can be returned by calling with addAlphaLayer=True.
      
    If the given source image has alpha, a new image without alpha
      can be returned by calling with delAlphaLayer=True.
      
    """
    
    wxImage = wx.EmptyImage( *pilImage.size  )      # Has no transparency plane.
    
    pilMode = pilImage.mode
    hasAlpha = pilImage.mode[-1] == 'A'
    
    if hasAlpha or (not hasAlpha and addAlphaLayer) :
        
        pilImageRGBA = pilImage.copy()  # Image mode might now be RGB, not RGBA.
        if pilImage.mode != 'RGBA' :
            pilImageRGBA = pilImageRGBA.convert( 'RGBA' )
        #end if
        # The image mode is now gauranteed to be RGBA.
        
        pilImageStr = pilImageRGBA.tostring()    # Convert all 4 image planes.
        
        # Extract just the RGB data
        pilRgbStr = pilImageRGBA.copy().convert( 'RGB').tostring()
        wxImage.SetData( pilRgbStr )
        
        # Extract just the existing pilImage alpha plane data.
        pilAlphaStr = pilImageStr[3::4]      # start at index 3 with a stride (skip) of 4.
        wxImage.SetAlphaData( pilAlphaStr )
        
    elif delAlphaLayer or ((not hasAlpha) and (not addAlphaLayer)) :
        
        pilImageRGB = pilImage.copy().convert( 'RGB' )
        
        wxImage.SetData( pilImageRGB.tostring() )     
        
    else :      # All undecoded parameter permutations.  This is for DEBUG.
        
        print '####  ERROR:  ImgConv:WxImageFromPilImage():   Undecoded Parameter Combination.'
        sys.exit(1)
        
    #end if
    
    return wxImage
    
#end def

#----------------------------

def WxBitmapFromWxImage( wxImage, threshold=128 ) :    
    """
    2 ==> 1
    
    Any transparency mask or alpha transparency will be copied, to0.
    """
    
    wxBmap = wxImage.ConvertToBitmap()
    
    return wxBmap
    
#end def

#----------------------------

def WxImageFromWxBitmap( wxBmap ) :              
    """
    1 ==> 2
    
    Any transparency mask or alpha transparency will be copied, to0.
    """
    return wx.ImageFromBitmap( wxBmap )
#end def

#------------------------------------------------------------------------------

def PilImageFromWxBitmap( wxBmap, keepTransp=True, createTransp=False, debug=False ) :
    """
    1 ==> 3
    
    The default parameter values preserve any existing transparency, 
      but do not add one if it none exists in the source image.
    
    If the given source has no alpha, a new image with alpha transparency 
      can be returned by calling with addAlphaLayer=True.
      
    If the given source image has alpha, a new image without alpha
      can be returned by calling with delAlphaLayer=True.
      
    """
    
    wxImage = WxImageFromWxBitmap( wxBmap )
    
    return PilImageFromWxImage( wxImage )   # Always preserves any transparency.
#end def

#----------------------------

def PilImageFromWxImage( wxImage, keepTransp=True, createTransp=False, debug=False ) :
    """
    2 ==> 3  
    
    Default preserves any transparency.
    """
    
    # These can never be simultaneous.
    hasMask  = wxImage.HasMask()
    hasAlpha = wxImage.HasAlpha()
    if debug :
        print '>>>>  PilImageFromWxImage():  Input Image has Alpha'
    
    # Always convert a mask into an aplha layer.
    # Deal with keeping or discarding this alpha later on.
    if hasMask :    # Is always mutually exclusive with hasAlpha.
        
        if debug :
            print '>>>>  PilImageFromWxImage():  Converting Input Image Mask to Alpha'
        
        wxImage.InitAlpha()     # Covert the separate mask to a 4th alpha layer.
        hasAlpha = True
            
    #end if 
    
    image_size = wxImage.GetSize()      # All images here have the same size.
    
    # Create an RGB pilImage and stuff it with RGB data from the wxImage.
    pilImage = Image.new( 'RGB', image_size )
    pilImage.fromstring( wxImage.GetData() )
    
    # May need the separated planes if an RGBA image is needed. later.
    r_pilImage, g_pilImage, b_pilImage = pilImage.split()
        
    if hasAlpha :
        
        if keepTransp : # Ignore createTransp - has no meaning
            
            if debug :
                print '>>>>  PilImageFromWxImage():  Keeping Transparency'

            # Must recompose the pilImage from 4 layers.
            r_pilImage, g_pilImage, b_pilImage = pilImage.split()

            # Create a Black L pilImage and stuff it with the alpha data 
            #   extracted from the alpha layer of the wxImage.
            pilImage_L = Image.new( 'L', image_size )
            pilImage_L.fromstring( wxImage.GetAlphaData() )

            # Create an RGBA PIL image from the 4 layers.
            pilImage = Image.merge( 'RGBA', (r_pilImage, g_pilImage, b_pilImage, pilImage_L) )
        
        elif (not keepTransp) :
            
            # The RGB pilImage can be returned as it is now.
            if debug :
                print '>>>>  PilImageFromWxImage():  Returning an RGB PIL Image.'
            
        #end if
        
    elif (not hasAlpha) and createTransp :      # Ignore keepTransp - has no meaning
        
        if debug :
            print '>>>>  PilImageFromWxImage():  Creating a New Transparency Layer'
        
        # Create a Black L mode pilImage. The resulting image will still
        #  look the same, but will allow future transparency modification.
        pilImage_L = Image.new( 'L', image_size )
        
        # Create an RGBA pil image from the 4 bands.
        pilImage = Image.merge( 'RGBA', (r_pilImage, g_pilImage, b_pilImage, pilImage_L) )
        
    #end if
    
    return pilImage
    
#end PilImageFromWxImage def

#------------------------------------------------------------------------------
