Texture Helper - A UnityScript library to manipulate textures and pixmaps!

Texture Helper is NOW AVAILABLE
Available now on the Unity Asset Store: - Now Only $10 :smile: - Unity Asset Store - The Best Assets for Game Making

Texture Helper is a handy collection of UnityScript sourcecode functions for creating and manipulating textures and pixmaps at runtime. A pixmap (pixel map, or bitmap) stores an image in main memory for easy manipulation from scripts.

Features

  • Procedurally generate textures and pixmap at runtime

  • Load (PNG/JPG from Resource/File/Url) and save (PNG/JPG/BMP/RAW) textures and pixmaps at runtime

  • Grab from the the screen into a texture, pixmap, or straight to a file (PNG/JPG/BMP/RAW)

  • Draw directly on textures and pixmaps with lines, circles, boxes, rects and ellipses

  • Includes ability to save in JPEG and BMP formats, plus a RAW memory dump

  • Automatically handles Float-based and Byte-based pixel-color formats

  • Easy ‘procedural’ programming interface e.g. CreateTexture(512,512);

  • Works with Unity Free, Unity Pro, Mobile and more (requires Unity 3.5.1 to purchase)

  • Over 110 commented functions

  • Many functions support multiple variations of input

  • Pixmaps can communicate with ARGB32-format isReadable textures only

Benefits

  • Really useful when you’re generating procedural geometry/content

  • A great help for modifying images, creating images or for loading and saving images at runtime

  • Handy for grabbing and saving generated imagery at runtime

  • Save yourself time writing 2500 lines of code!

  • Easy transfer between the screen, textures, pixmaps and files

  • Easy-to-use API saves time typing commands

  • Easy-to-understand Unity Script sourcecode included

  • Draw many pixels faster to a pixmap than directly to a texture (still requires SetPixels() to upload to texture)

A simple demo is included, showing drawing on a newly generated pixmap which is then transferred to a texture:

Full List of Functions

    function CreateTexture() {
        //Create a new Texture2D texture the size of the screen
        //The texture will be in ARGB32 format with no mipmaps
    
    function CreateTexture(Width:int, Height:int) {
        //Create a new Texture2D texture of a given size
        //The texture will be in ARGB32 format with no mipmaps

    function CreateTexture(Width:int, Height:int, Format:TextureFormat) {
        //Create a new Texture2D texture of a given size and format
        //The texture will have no mipmaps

    function CreateTexture(Width:int, Height:int, Format:TextureFormat, MipMap:boolean) {
        //Create a new Texture2D texture of a given size and format with or without mipmaps

    function CreateTexture(Width:int, Height:int, Format:TextureFormat, MipMap:boolean, Filter:FilterMode, AnisotropicLevel:int, Wrap:TextureWrapMode, Bias:float) {
        //Create a new Texture2D texture of a given size and format with or without mipmaps
        //Also assign a filter mode, anisotropic level, wrapping mode and mipmap bias
    
    function FreeTexture(Tex:Texture2D) {
        //Delete a Texture2D
            
    function ResourceToTexture(ResourcePath:String) {
        //Load an image from a Resources folder into a Texture2D texture
        //The image format must be a JPEG (RGB24) or a PNG (RGB24 or RGBA32)
        //The image resource being loaded must have its extension renamed from .jpg/.png to .bytes
        //JPEG files will return an RGB24 Texture2D without any alpha channel
        //PNG files will return an ARGB32 Texture2D including alpha channel regardless of whether the file has an alpha channel (defaults to 255)
    
    function ResourceToTexture(ResourcePath:String, DXTCompress:boolean) {
        //Load an image from a Resources folder into a Texture2D texture
        //The texture will be optionally DXT compressed - JPEGs will be DXT1 compressed without alpha, PNGs will be DXT5 compressed with alpha
        //The texture will not be mipmapped
        //The image format must be a JPEG (RGB24) or a PNG (RGB24 or RGBA32)
        //The image resource being loaded must have its extension renamed from .jpg/.png to .bytes
        //JPEG files will return an RGB24 Texture2D without any alpha channel
        //PNG files will return an ARGB32 Texture2D including alpha channel regardless of whether the file has an alpha channel (defaults to 255)

    function ResourceToTexture(ResourcePath:String, DXTCompress:boolean, MipMap:boolean) {
        //Load an image from a Resources folder into a Texture2D texture
        //The texture will be optionally DXT compressed - JPEGs will be DXT1 compressed without alpha, PNGs will be DXT5 compressed with alpha
        //The texture will be optionally mipmapped
        //The image format must be a JPEG (RGB24) or a PNG (RGB24 or RGBA32)
        //The image resource being loaded must have its extension renamed from .jpg/.png to .bytes
        //JPEG files will return an RGB24 Texture2D without any alpha channel
        //PNG files will return an ARGB32 Texture2D including alpha channel regardless of whether the file has an alpha channel (defaults to 255)

    function FileToTexture(FilePathUrl:String, Tex:Texture2D) {
        //Load an image file from disk into an existing Texture2D texture, e.g. call CreateTexture() first
        //Image file must be a regular .JPG or .PNG
        //JPEG will load as RGB24 format, PNG will load as ARGB32
        //Since `WWW` is used, you can also load from a website url
        //FilePathUrl should begin with http:// or similar for web-based images, or file:// for filesystem/disk images
        //Texture size and format may be modified afterwards
        //If original texture had DXT1 or DXT5 compression the loaded texture will be compressed (JPEG as DXT1, PNG as DXT5)
    
    function LoadTexture2D(ResourcePath:String) {
        //Load a texture asset from the Resources folder into a Texture2D object
        //The texture should already have been set up in the Unity editor with appropriate import settings - it's a Texture2D not an image file
    
    function CloneTexture(SourceTexture:Texture2D) {
        //Create an exact copy of a texture including its pixels
        //SourceTexture must be isReadable and in ARGB32 format
        //Works using a pixmap to transfer the data so must be a format compatible with Pixmaps ie ARGB32

    function SaveTexturePNG(Tex:Texture2D, FilePath:String) {
        //Save a readable texture as a PNG file
        //This only works with ARGB32 or RGB24 Texture2D, no other formats
        //Typicall file extension is .png
    
    function SaveTextureJPG(Tex:Texture2D, FilePath:String) {
        //Save a readable texture as a JPG file - 85% quality
        //This only works with ARGB32 or RGB24 Texture2D, no other formats
        //Typical file extension is .jpg
    
    function SaveTextureJPG(Tex:Texture2D, FilePath:String, Quality:int) {
        //Save a readable texture as a JPG file at a given quality
        //Quality should range from 1 to 100 (as percentage, 100=maximum quality)
        //This only works with ARGB32 or RGB24 Texture2D, no other formats
        //Typical file extension is .jpg
    
    function SaveTextureRAW(Tex:Texture2D, FilePath:String) {
        //Save a readable texture as a RAW file (raw byte pixel data, no header)
        //Will include alpha channel even if the texture had none (in which case alpha is 255)
        //Typical file extension is .raw (but not camera raw format)

    function SaveTextureBMP(Tex:Texture2D, FilePath:String) {
        //Save a readable texture as a BMP file (raw byte pixel data, no header)
        //Will NOT include alpha channel even if the texture had one
        //Typical file extension is .bmp
    
    function GrabTexture() {
        //Grab the backbuffer into a new ARGB32 texture
        //Returns the new texture
    
    function GrabTexture(Tex:Texture2D) {
        //Grab the backbuffer into an existing texture
        //Requires an ARGB32 and RGB24 texture format. The texture also has to have Is Readable flag set in the import settings
    
    function GrabTexture(X:int, Y:int, Width:int, Height:int) {
        //Grab a portion of the backbuffer into a new ARGB32 texture
        //Returns the new texture
    
    function GrabTexture(Tex:Texture2D, X:int, Y:int, Width:int, Height:int) {
        //Grab a portion of the backbuffer into an existing texture
        //Requires an ARGB32 and RGB24 texture format. The texture also has to have Is Readable flag set in the import settings

    function GrabTexture(X:int, Y:int, Width:int, Height:int, ToX:int, ToY:int, MipMaps:boolean) {
        //Grab a portion of the backbuffer into a new ARGB32 texture at a given offset
        //Optionally generates mipmaps
        //Returns the new texture
    
    function GrabTexture(Tex:Texture2D, X:int, Y:int, Width:int, Height:int, ToX:int, ToY:int, MipMaps:boolean) {
        //Grab a portion of the backbuffer into an existing texture at a given offset
        //Optionally generates mipmaps
        //Requires an ARGB32 and RGB24 texture format. The texture also has to have Is Readable flag set in the import settings

    function TextureWidth(Tex:Texture2D) {
        //Get the width of a Texture2D texture in texels

    function TextureWidth(Tex:Texture2D, NewWidth:int) {
        //Set the width of a Texture2D texture in texels
        //The pixel content of the texture will likely be trashed afterwards
        //The new width must be a texture size allowed by the graphics card e.g. 1..4096
        
    function TextureHeight(Tex:Texture2D) {
        //Get the height of a Texture2D texture in texels

    function TextureHeight(Tex:Texture2D, NewHeight:int) {
        //Set the height of a Texture2D texture in texels
        //The pixel content of the texture will likely be trashed afterwards
        //The new height must be a texture size allowed by the graphics card e.g. 1..4096
        
    function TextureFilter(Tex:Texture2D) {
        //Get the filterMode of a Texture2D texture
        //Can be FilterMode.Point, FilterMode.Bilinear, FilterMode.Trilinear

    function TextureFilter(Tex:Texture2D, NewFilter:FilterMode) {
        //Set the filterMode of a Texture2D texture
        //Can be FilterMode.Point, FilterMode.Bilinear, FilterMode.Trilinear

    function TextureAniso(Tex:Texture2D) {
        //Get the amount of anisotropic filtering for a Texture2D texture
        //Can be in the range 1-9 where 1=none and 9=maximum
    
    function TextureAniso(Tex:Texture2D, NewAnisotropic:int) {
        //Set the amount of anisotropic filtering for a Texture2D texture
        //Can be in the range 1-9 where 1=none and 9=maximum
    
    function TextureWrap(Tex:Texture2D) {
        //Get the wrap ode of a Texture2D texture
        //Can be TextureWrapMode.Clamp or TextureWrapMode.Wrap

    function TextureWrap(Tex:Texture2D, NewWrapMode:TextureWrapMode) {
        //Set the wrap ode of a Texture2D texture
        //Can be TextureWrapMode.Clamp or TextureWrapMode.Wrap
        
    function TextureBias(Tex:Texture2D) {
        //Get the mipMapBias of a Texture2D texture
        //Returns float in the range -1 to +1
    
    function TextureBias(Tex:Texture2D, NewBias:float) {
        //Set the mipMapBias of a Texture2D texture
        //Float value should be in the range -1 to +1, most likely -0.5 to +0.5

    function TextureMipMaps(Tex:Texture2D) {
        //Get the number of Mip Map levels in a Texture2D texture, as an integer
        //Includes the biggest level size, so is always 1 or more
    
    function TextureMipMaps(Tex:Texture2D, MipMap:boolean) {
        //Change a Texture2D texture to have mipmaps if it doesn't already
        //This will alter the texture and possibly trash the pixel content!

    function TextureMode(Tex:Texture2D) {
        //Get the format of an existing Texture2D texture
        //Returns the TextureFormat

    function TextureMode(Tex:Texture2D, NewFormat:TextureFormat) {
        //Set the format of an existing Texture2D texture
        //This will alter the texture and possibly trash the pixel content!

    function TextureMode(Tex:Texture2D, NewFormat:TextureFormat, NewWidth:int, NewHeight:int) {
        //Set the format of an existing Texture2D texture and also resize it
        //This will alter the texture and possibly trash the pixel content!
        //The new size must be a texture size allowed by the graphics card e.g. 1..4096
    
    function TextureMode(Tex:Texture2D, NewFormat:TextureFormat, NewWidth:int, NewHeight:int, MipMaps:boolean) {
        //Set the format of an existing Texture2D texture and also resize it, possibly with mipmaps
        //This will alter the texture and possibly trash the pixel content!
        //The new size must be a texture size allowed by the graphics card e.g. 1..4096
            
    function TextureCompression(Tex:Texture2D) {
        //Get the compressed status of an existing Texture2D texture
        //Typically can only be applied to RGB24 or ARGB32 uncompressed textures
        //RGB24 is compressed with DXT1, ARGB32 is compressed with DXT5
        //Returns true if compressed, false if not
    
    function TextureCompression(Tex:Texture2D, HighQuality:boolean) {
        //Apply DXT compression to an existing Texture2D texture
        //If HighQuality is true then the texture is dithered first to provide better results, but runs slower
        //Typically can only be applied to RGB24 or ARGB32 uncompressed textures
        //RGB24 is compressed with DXT1, ARGB32 is compressed with DXT5

    function TexturePixel(Tex:Texture2D, X:int, Y:int, WithColor:Color, ApplyChanges:boolean) {
        //Draw a single pixel to a texture in a given color
        //Pixels will only upload to the actual texture if ApplyChanges=true
    
    function TextureRect(Tex:Texture2D, X1:int, Y1:int, X2:int, Y2:int, WithColor:Color, ApplyChanges:boolean) {
        //Draw an axis-aligned rectangle within a texture in a given color
        //Pixels in X and X2 columns are included, pixels in Y and Y2 rows are included
        //Size of the rectangle will be cropped to fit within the texture
        //Pixels will only upload to the actual texture if ApplyChanges=true
    
    function TextureBox(Tex:Texture2D, X1:int, Y1:int, X2:int, Y2:int, WithColor:Color, ApplyChanges:boolean) {
        //Draw a hollow box in a Texture in a given color
        //Pixels will only upload to the actual texture if ApplyChanges=true
    
    function TextureLine(Tex:Texture2D, X1:int, Y1:int, X2:int, Y2:int, WithColor:Color, ApplyChanges:boolean) {
        //Draw an aliased/pixel line at any angle in a Texture in a given color
        //Based on bresenham integer-math routine from Wikipedia
         //Pixels will only upload to the actual texture if ApplyChanges=true

    function TextureEllipse(Tex:Texture2D, XCenter:int, YCenter:int, XRadius:int, YRadius:int, WithColor:Color, ApplyChanges:boolean) {
        //Draw a filled axis-aligned ellipse within a Texture using the given color
        //Centered at XCenter,YCenter with radii of XRadius and YRadius
        //Based on mid-point ellipse algorithm, almost entirely integer math
         //Pixels will only upload to the actual texture if ApplyChanges=true
    
    function GrabPNG(FilePath:String){
        //Grab a screenshot of the backbuffer and save as a PNG file, exact dimensions
        //Suggested file format extension .png
    
    function GrabPNG(FilePath:String, Scale:int){
        //Grab a screenshot of the backbuffer and save as a PNG file, scaled to a larger size
        //Suggested file format extension .png
        //If Scale=1, normal size is grabbed. If Scale>1, double/triple/quad etc size is grabbed (if camera complies)
        //If Scale=0 a half-sized image (bilinear filtered) is grabbed
    
    function GrabJPG(FilePath:String) {
        //Grab a screen shot of the backbuffer and save it into a JPEG file
        //Suggested file format extension: .jpg
        //Defaults to JPEG quality 85%, normal scale

    function GrabJPG(FilePath:String, Quality:int) {
        //Grab a screen shot of the backbuffer and save it into a JPEG file
        //Suggested file format extension: .jpg
        //Quality ranges from 0 to 100, 100 is full quality
        GrabJPG(FilePath,Mathf.Clamp(Quality,1,100),1);                //Grab JPEG, normal scale
    }
    
    function GrabJPG(FilePath:String, Quality:int, Scale:int) {
        //Grab a screen shot of the backbuffer and save it into a JPEG file
        //Suggested file format extension: .jpg
        //Quality ranges from 0 to 100, 100 is full quality
        //If Scale=0, the image will be scaled down to half of its original size before being saved
        //If Scale>=1 the image will not be scaled (cannot scale up)

    function GrabRAW(FilePath:String) {
        //Grab a screen shot of the backbuffer and save it into a RAW file (raw data, not camera raw format)
        //Suggestion file format extension: .raw
    
    function GrabRAW(FilePath:String, Scale:int) {
        //Grab a screen shot of the backbuffer and save it into a RAW file (raw data, not camera raw format)
        //Suggestion file format extension: .raw
        //If Scale=0, the image will be scaled down to half of its original size before being saved
        //If Scale>=1 the image will not be scaled (cannot scale up)

    function GrabBMP(FilePath:String) {
        //Grab a screen shot of the backbuffer and save it into a BMP file
        //Suggested file format extension: .bmp
    
    function GrabBMP(FilePath:String, Scale:int) {
        //Grab a screen shot of the backbuffer and save it into a BMP file
        //Suggested file format extension: .bmp
        //If Scale=0, the image will be scaled down to half of its original size before being saved
        //If Scale>=1, the image will not be scaled (cannot scale up)

    function CreatePixmap() {
        //Create a new pixmap measuring Screen.width x Screen.height pixels in RGBA8 format (Color32[] array)
    
    function CreatePixmap(RGBA8:boolean) {
        //Create a new pixmap measuring Screen.width x Screen.height pixels
        //If RGBA8 is True then the pixmap will be in RGBA8 format, ie 4 bytes per pixel as a Color32[] array
        //If RGBA8 is False then the pixmap will be in RGBA32 format, ie 4 floats per pixel as a Color[] array
    
    function CreatePixmap(Width:int, Height:int) {
        //Create a new pixmap measuring width x height pixels in RGBA8 format (color32[] array)
    
    function CreatePixmap(Width:int, Height:int, RGBA8:boolean) {
        //Create a new pixmap measuring width x height pixels
        //If RGBA8 is True then the pixmap will be in RGBA8 format, ie 4 bytes per pixel as a Color32[] array
        //If RGBA8 is False then the pixmap will be in RGBA32 format, ie 4 floats per pixel as a Color[] array
        
    function FreePixmap(PixmapToFree:Pixmap) {
        //Free a previously created pixmap - frees up the memory
        //To really Free it all references to the Pixmap instance must be nulled so the Garbage Collector can remove it
    
    function ClearPixmap(PixmapToClear:Pixmap) {
        //Erase the contents of all pixels in the pixmap - they will be set to zero's (black with no alpha)
    
    function ClearPixmap(PixmapToClear:Pixmap, Red:float, Green:float, Blue:float, Alpha:float) {
        //Erase the contents of all pixels in the pixmap - they will be set the color values specified
        //Expects to be used with a Color[] float format pixmap
    
    function ClearPixmap(PixmapToClear:Pixmap, Red:byte, Green:byte, Blue:byte, Alpha:byte) {
        //Erase the contents of all pixels in the pixmap - they will be set the color values specified
        //Expects to be used with a Color32[] byte format pixmap
    
    function ConvertPixmap(PixmapToConvert:Pixmap, Format:int) {
        //Convert a pixmap between Color[] and Color32[] formats
        //Format=0 means Color[] float array
        //Format=1 means Color32[] byte array
        //A new array will be created, the data is converted, then the old array is released
    
    function ClonePixmap(SourcePixmap:Pixmap) {
        //Create a duplicate copy of a pixmap including its pixel data in the same format
        //Returns the new pixmap

    function CopyPixmap(SourcePixmap:Pixmap, DestPixmap:Pixmap) {
        //Copy pixels from one pixmap to another
        //Pixmaps must both be the same size

    function CopyPixmap(SourcePixmap:Pixmap, DestPixmap:Pixmap, SourceX:int, SourceY:int, SourceWidth:int, SourceHeight:int, DestX:int, DestY:int) {
        //Copy an area of pixels from one pixmap to another
        //Does not perform bounds error checking, make sure you only copy from a source rectangle that fits within the source Pixmap,
        //and make sure you have enough room in the destination Pixmap to accept the entire SourceWidth x SourceHeight pixels being copied.

    function PixmapWidth(PixmapToGet:Pixmap) {
        //Return the width of a pixmap
    
    function PixmapHeight(PixmapToGet:Pixmap) {
        //Return the height of a pixmap
    
    function PixmapFormat(PixmapToGet:Pixmap) {
        //Return the format of a pixmap
        //0=Color[] float format
        //1=Color32[] byte format
        //-1 = no pixmap
    
    function PixmapFormat(PixmapToChange:Pixmap, NewFormat:int) {
        //Change the format of a pixmap to a new format
        //This is really a wrapper for ConvertPixmap, keeping with consistency of calling
    
    function PixmapBytes(PixmapToMeasure:Pixmap) {
        //Return how many bytes the pixels in a pixmap consume
    
    function XFlipPixmap(Pix:Pixmap) {
        //Horizontally flip the columns in a Pixmap, ie flip the image side-to-side
        //Works in place with no extra buffers, does not create a second Pixmap so use ClonePixmap first if you need to preserve the original
    
    function YFlipPixmap(Pix:Pixmap) {
        //Vertically flip the rows in a Pixmap, ie turn the image upside down
        //Works in place with no extra buffers, does not create a second Pixmap so use ClonePixmap first if you need to preserve the original
        //This may be useful for preparing pixmaps for upload to a texture since textures have an opposite Y coordinate system
    
    function PixmapPixelCount(PixmapToMeasure:Pixmap) {
        //Return how many pixels the pixmap contains
    
    function PixmapPixels(PixmapToGet:Pixmap) {
        //Return the Color[] or Color32[] array containing pixel data for this pixmap
    
    function PixmapPixel(PixmapToAccess:Pixmap, XPos:int, YPos:int) {
        //Read a pixel from a pixmap
    
    function PixmapPixel(PixmapToAccess:Pixmap, XPos:int, YPos:int, Pixel:Color) {
        //Write a Color float pixel to a pixmap
    
    function PixmapPixel(PixmapToAccess:Pixmap, XPos:int, YPos:int, Pixel:Color32) {
        //Write a Color32 byte pixel to a pixmap
    
    function PixmapPixel(PixmapToAccess:Pixmap, XPos:int, YPos:int, Red:float, Green:float, Blue:float, Alpha:float) {
        //Write a Color float pixel to a pixmap
    
    function PixmapPixel(PixmapToAccess:Pixmap, XPos:int, YPos:int, Red:byte, Green:byte, Blue:byte, Alpha:byte) {
        //Write a Color32 byte pixel to a pixmap
    
    function PixmapPixelFast(PixmapToAccess:Pixmap, XPos:int, YPos:int) {
        //Read a pixel from a pixmap without any safety checks, for speed
        //Only works with Color[] float format pixmaps
        //May throw a runtime error if trying to access a Color32[] format pixmap or a pixmap doesn't exist or the coords are out of bounds

    function PixmapPixelFast(PixmapToAccess:Pixmap, XPos:int, YPos:int, Pixel:Color) {
        //Write a Color float pixel to a pixmap without any safety checks, for speed
        //Only works with Color[] float format pixmaps
        //May throw a runtime error if trying to access a Color32[] format pixmap or a pixmap doesn't exist or the coords are out of bounds
    
    function PixmapPixelFast(PixmapToAccess:Pixmap, XPos:int, YPos:int, Pixel:Color32) {
        //Write a Color32 byte pixel to a pixmap without any safety checks, for speed
        //Only works with Color[] float format pixmaps
        //May throw a runtime error if trying to access a Color32[] format pixmap or a pixmap doesn't exist or the coords are out of bounds
    
    function PixmapPixelFast(PixmapToAccess:Pixmap, XPos:int, YPos:int, Red:float, Green:float, Blue:float, Alpha:float) {
        //Write a Color float pixel to a pixmap without any safety checks, for speed
        //Only works with Color[] float format pixmaps
        //May throw a runtime error if trying to access a Color32[] format pixmap or a pixmap doesn't exist or the coords are out of bounds
    
    function PixmapPixelFast(PixmapToAccess:Pixmap, XPos:int, YPos:int, Red:byte, Green:byte, Blue:byte, Alpha:byte) {
        //Write a Color32 byte pixel to a pixmap without any safety checks, for speed
        //Only works with Color[] float format pixmaps
        //May throw a runtime error if trying to access a Color32[] format pixmap or a pixmap doesn't exist or the coords are out of bounds
    
    function PixmapPixelFast32(PixmapToAccess:Pixmap, XPos:int, YPos:int) {
        //Read a pixel from a pixmap without any safety checks, for speed
        //Only works with Color32[] byte format pixmaps
        //May throw a runtime error if trying to access a Color[] format pixmap or a pixmap doesn't exist or the coords are out of 
    
    function PixmapPixelFast32(PixmapToAccess:Pixmap, XPos:int, YPos:int, Pixel:Color) {
        //Write a Color32 float pixel to a pixmap without any safety checks, for speed
        //Only works with Color32[] byte format pixmaps
        //May throw a runtime error if trying to access a Color[] format pixmap or a pixmap doesn't exist or the coords are out of bounds
    
    function PixmapPixelFast32(PixmapToAccess:Pixmap, XPos:int, YPos:int, Pixel:Color32) {
        //Write a Color32 float pixel to a pixmap without any safety checks, for speed
        //Only works with Color32[] byte format pixmaps
        //May throw a runtime error if trying to access a Color[] format pixmap or a pixmap doesn't exist or the coords are out of bounds
    
    function PixmapPixelFast32(PixmapToAccess:Pixmap, XPos:int, YPos:int, Red:float, Green:float, Blue:float, Alpha:float) {
        //Write a Color32 float pixel to a pixmap without any safety checks, for speed
        //Only works with Color32[] byte format pixmaps
        //May throw a runtime error if trying to access a Color[] format pixmap or a pixmap doesn't exist or the coords are out of bounds
    
    function PixmapPixelFast32(PixmapToAccess:Pixmap, XPos:int, YPos:int, Red:byte, Green:byte, Blue:byte, Alpha:byte) {
        //Write a Color32 float pixel to a pixmap without any safety checks, for speed
        //Only works with Color32[] byte format pixmaps
        //May throw a runtime error if trying to access a Color[] format pixmap or a pixmap doesn't exist or the coords are out of bounds
    
    function PixmapRect(Pix:Pixmap, X1:int, Y1:int, X2:int, Y2:int, WithColor:Color) {
        //Draw an axis-aligned rectangle within a pixmap in a given color
        //Pixels in X and X2 columns are included, pixels in Y and Y2 rows are included
        //Size of the rectangle will be cropped to fit within the Pixmap
    
    function PixmapBox(Pix:Pixmap, X1:int, Y1:int, X2:int, Y2:int, WithColor:Color) {
        //Draw a hollow box in a Pixmap in a given color
    
    function PixmapLine(Pix:Pixmap, X1:int, Y1:int, X2:int, Y2:int, WithColor:Color) {
        //Draw an aliased/pixel line at any angle in a Pixmap in a given color
        //Based on bresenham integer-math routine from Wikipedia
 
    function PixmapEllipse(Pix:Pixmap, XCenter:int, YCenter:int, XRadius:int, YRadius:int, WithColor:Color) {
        //Draw a filled axis-aligned ellipse within a Pixmap using the given color
        //Centered at XCenter,YCenter with radii of XRadius and YRadius
        //Based on mid-point ellipse algorithm, almost entirely integer math

    function ResourceToPixmap(ResourcePath:String, RGBA8:boolean) {
        //Load an image from a Resources folder into a Pixmap
        //The image format must be a JPEG (RGB24) or a PNG (RGB24 or RGBA32)
        //The image resource being loaded must have its extension renamed from .jpg/.png to .bytes
        //RGBA8 determines whether to load in Color[] float format or Color32[] byte format
    
    function FileToPixmap(FilePathUrl:String, RGBA8:boolean) {
        //Load an image file from disk into an new Pixmap
        //You do not need to know the size of the image before loading it, but more cpu time will be blocked while waiting for the load
        //Image file must be a regular .JPG or .PNG
        //JPEG will load as RGB24 format, PNG will load as ARGB32
        //Since `WWW` is used, you can also load from a website url
        //FilePathUrl should begin with http:// or similar for web-based images, or file:// for filesystem/disk images
        //RGBA8 determines whether to load in Color[] float format or Color32[] byte format
        //Recommended not to use this in a Unity webplayer because empty wait loop will block/hog all cpu time preventing the download

    function FileToPixmap(FilePathUrl:String, Pix:Pixmap, RGBA8:boolean) {
        //Load an image file from disk into an existing Pixmap, e.g. call CreatePixmap() first with same size as the image being loaded
        //You will need to know the size of the image before loading it
        //Image file must be a regular .JPG or .PNG
        //JPEG will load as RGB24 format, PNG will load as ARGB32
        //Since `WWW` is used, you can also load from a website url
        //FilePathUrl should begin with http:// or similar for web-based images, or file:// for filesystem/disk images
        //RGBA8 determines whether to load in Color[] float format or Color32[] byte format
        //Texture size and Pixmap size must be the same
    
    function SavePixmapPNG(PixmapToSave:Pixmap, FilePath:String){
        //Save a pixmap as a PNG file
        //Suggested file format extension .png
        //Pixmap has to be converted to a texture first then encoded to PNG and saved
        //Returns true if successful, otherwise false
    
    function SavePixmapJPG(PixmapToSave:Pixmap, FilePath:String) {
        //Save a pixmap into a JPEG file
        //Suggested file format extension: .jpg
        //Saves with 85% JPEG quality
        //Pixmap has to be converted to a texture first then encoded to JPG and saved
        //Returns true if successful, otherwise false
    
    function SavePixmapJPG(PixmapToSave:Pixmap, FilePath:String, Quality:int) {
        //Save a pixmap into a JPEG file
        //Suggested file format extension: .jpg
        //Quality ranges from 1 to 100%
        //Pixmap has to be converted to a texture first then encoded to JPG and saved
        //Returns true if successful, otherwise false
    
    function SavePixmapRAW(PixmapToSave:Pixmap, FilePath:String) {
        //Save a pixmap into a RAW file (raw data, not camera raw format)
        //Suggestion file format extension: .raw
        //Returns true if successful, otherwise false
    
    function SavePixmapBMP(PixmapToSave:Pixmap, FilePath:String) {
        //Save a pixmap into a BMP file
        //Suggested file format extension: .bmp
        //BMP does not support Alpha channel, it is ignored, RGB only
        //Returns true if successful, otherwise false

    function TextureToPixmap(TextureToRead:Texture2D, PixmapToWrite:Pixmap) {
        //Download pixels from a Texture2D into a pixmap
        //Texture2D must have isReadable flag set and be a compatible format ARGB32, RGB or Alpha8
        //The whole texture will be imported, so pixmap size and texture size must match
        //MipMap level 0 only will be read

    function TextureToPixmap(TextureToRead:Texture2D, PixmapToWrite:Pixmap, MipLevel:int) {
        //Download pixels from a Texture2D into a pixmap
        //Texture2D must have isReadable flag set and be a compatible format ARGB32, RGB or Alpha8
        //The whole texture will be downloaded, so pixmap size and texture size must match
        //MipMap level specified will be read
    
    function PixmapToTexture(PixmapToRead:Pixmap, TextureToWrite:Texture2D) {
        //Upload pixels from a pixmap into a Texture2D
        //Texture2D must have isReadable flag set and be a compatible format ARGB32, RGB or Alpha8
        //The whole texture will be uploaded, so pixmap size and texture size must match
        //MipMap level 0 only will be written

    function PixmapToTexture(PixmapToRead:Pixmap, TextureToWrite:Texture2D, MipLevel:int) {
        //Upload pixels from a pixmap into a Texture2D
        //Texture2D must have isReadable flag set and be a compatible format ARGB32, RGB or Alpha8
        //The whole texture will be uploaded, so pixmap size and texture size must match
        //MipMap level 0 only will be written
    
    function GrabPixmap() {
        //Grab the backbuffer into a temporary ARGB32 texture and transfer it to a new Pixmap in RGBA8 format
        //Returns the new Pixmap
    
    function GrabPixmap(Pix:Pixmap) {
        //Grab the backbuffer into an existing pixmap
    
    function GrabPixmap(X:int, Y:int, Width:int, Height:int) {
        //Grab a portion of the backbuffer into a new ARGB32 texture and transfer it to a new Pixmap in RGBA8 format
        //Returns the new Pixmap
    
    function GrabPixmap(Pix:Pixmap, X:int, Y:int, Width:int, Height:int) {
        //Grab a portion of the backbuffer into an existing pixmap

    function GrabPixmap(X:int, Y:int, Width:int, Height:int, ToX:int, ToY:int) {
        //Grab a portion of the backbuffer into a new ARGB32 texture at a given offset and transfer it to a new Pixmap in RGBA8 format
        //Returns the new Pixmap

    function GrabPixmap(Pix:Pixmap, X:int, Y:int, Width:int, Height:int, ToX:int, ToY:int) {
        //Grab a portion of the backbuffer into an existing Pixmap at a given offset

Buy it now on the Unity Asset Store: - Only $10 :smile:

1091897--40979--$thbig.jpg

Bump

Looks interesting. Questions:

Can writing to textures be done at runtime?
How does it perform on Mobile?
Have a video of it in action showing the benefits of using the tool?

Without demos, not too suprised no one as commented. Maybe post videos screenshots and/or examples of it in use?

To say, typically when looking at a texture tool, people want to see examples of… you know… textures used with the tool? (maybe “before and after” shots?).

Hi there.

Yes, I haven’t put enough time into the marketing/information yet. I wasn’t sure if anyone would be interested in this, once I finally submitted it, so I thought I’d let it coast for a little bit, plus have been busy with my next asset.

Writing to textures can be done at runtime. Now, like anything else in Unity, to write to a texture you have to at some point use SetPixels() and Apply() to make any changes. There is absolutely no way around that. So any performance from using that approach applies here also. I have included functions which let you draw axis-aligned ellipses, rectangles, boxes, points and lines at any angle … which draw directly to the texture using SetPixels. So again when you’re done with drawing you need to Apply() the changes (or tell one of the drawing functions to do so). This then uploads to video ram. It has to go over the graphics bus to do that.

Now, another way to draw on textures would be to use a RenderTexture which this does not support at all (because it’s a Pro-only feature and I don’t yet own a copy). Another option you could do is draw to the backbuffer and grab it back to a texture using ReadPixels().

I agree about needing to put in some screenshots. I did in fact include one, but for some reason in the past few weeks Unity’s image uploader for the forums has been acting up and not showing images properly… I will try again.

Yes actually there is a screenshot attached to the first post, but it just isn’t showing up no matter how many times I try to re-upload a new one.

Hmm report a bug? Also, think there may be a forum for website issues.

I forgot to answer your question about mobile. To my knowledge there is no reason why everything wouldn’t work on mobile, except perhaps some of the image loading/saving if there are issues on mobile or ways that you’re supposed to follow a certain official protocol to access the file system? Otherwise it’s standard unity stuff.

With mobile typically there is greater divide between “works” and “performs well and is usable”, so asking more to the latter.

As you know and have suggested, SetPixels is a heavy operation; even more apparent on limited devices such as mobile. Not sure how often you’re calling these heavy operations, but to be mobile friendly, might consider having some sort of limiter so that optionally such expensive calls are not made every frame.

I will be more specific.

The library provides numerous functions and none of those functions are being triggered off automatically. So it’s up to you what you use them for. If you want something to update every frame, then you would call an appropriate function every frame. But as it is the library itself doesn’t do anything over time, it’s just functionality for you to use in your scripts.

Functions which use SetPixels, ReadPixels or Apply are:

a) functions for converting a pixmap to a texture
b) functions for drawing on a texture directly
c) functions for grabbing to a texture from the backbuffer

Each of the drawing functions has an optional parameter to say whether you want it to Apply() after the drawing is done or whether to wait. If you wait, you can do multiple drawing operations without applying, requiring an Apply() at the end when you’re ready to upload to a texture.

So basically if you modify a texture in any way it’s going to use SetPixels, ReadPixels or Apply in some way eventually. These are not exactly quick operations, as most Unity users are aware - and part of that is because Unity’s implementation of moving data to/from textures is very slow (compared to other development platforms). For example the speed of Apply()'ing a texture (ie uploading changes to video ram) is several times slower than it should be. That’s a Unity issue. They implemented SetPixels32() for example to speed this up (and it does speed up SetPixels() operations about 3-4x), but overall these operations are still relatively slow and the Apply() operation is also relatively slow. Because of Unity slow implementation it’s an area that you have to be careful with even on the desktop. On mobile the upload to video ram is even slower and you might only be able to do a single 256x256 texture each frame at 60hz… I haven’t been able to find or get anyone to share any hard data about the performance. For me on a 2006 iMac (Core 2 Duo 2Ghz, ATI X1600 with 128mb video ram), Unity can only submit and apply changes to 1 x 512x512 texture per frame at 60hz, or just over. It should really be able to to do more like a 2048x2048 texture per frame (which I proved in BlitzMax for example which can easily accomplish that speed). On a fast/modern computer I could do that same 512x512 texture modify/upload (ie all pixels change) at around 400fps, so about 4x the performance, on an i7 cpu with built-in intelHD graphics. This is way under par but unfortunately all we have to work with.

If you are making something for mobile you really have to look at careful selection of algorithms such as splitting up the image to be modified into multiple small textures (keeping an eye on draw calls) and only uploading changes to the tiles that have been modfied in a given frame. Otherwise the performance will be hit hard. It’s unfortunate, I wish Unity would do something about this significant bottleneck - there is a lot of room for improvement.

In my speed tests, a byte-based pixmap/SetPixels32() is overall currently the fastest method to upload texture data. SetPixels() is about 3-4 times slower. Even Alpha8 textures are no faster (about the same speed actually) as using SetPixels32() simply because you have to store 4 FLOATS in a Color() format, even though only the Alpha is used, and then it has to read 4 bytes for the alpha value and convert the float to a byte, etc… so overall it really was not any faster than using full 32-bit color. Also, SetPixels32() for byte data was for me about 100fps faster than the SetPixels() that Alpha8 needs, before calling Apply(), because there is less data conversion overhead. So I was getting like 500fps versus 400fps for Alpha8. So it really doesn’t help to try using Alpha8 textures instead of 32-bit byte textures. Texture helper lets you store pixmaps in byte format or float format, so most of the time I’d recommend the byte format for significant speed purposes.

I hope that helps give you some idea. If you have UnityPro I’d recommend using RenderTextures if you need to modify a texture every frame, and use the GPU to do the modifications to it. … passing data across the graphics bus between main ram and video ram is a significant bottleneck.

No matter what I tried I couldn’t get images to show up in the original post, so here they are:

Hi TextureHelper!
1091897--40979--$thbig.jpg

Drawing directly to a texture at runtime (as shown in the included mini demo, this is not a RenderTexture):

Full list of all functions:



Nice package,

I’m on mobile and really need maximum performance.
Would you consider adding RenderTexture options for customers that do have Pro.
Many assets provide both Free/Pro features to cater to both types of users.

Cheers.

I think you can use render textures and normal textures interchangeably, so anything you can read/write with a normal texture you can also do to a RenderTexture? Correct me if I’m wrong.

What other render texture support are you envisioning?

The reason I asked is because I saw “Drawing directly to a texture at runtime (as shown in the included mini demo, this is not a RenderTexture):”
I thought that you are only using Unity Free features and that somehow RenderTexture would be different.

I don’t have Unity Pro so I don’t know for sure, but a render texture is basically a normal texture which has been assigned to a render buffer target so that the graphics pipeline will output to the texture instead of to the backbuffer. You can then take that rendered-to-texture and use it as the input to any normal shader, because it’s a normal texture.

Texture Helper can do some basic drawing to a texture, it’s basically a wrapper of some SetPixels() calls which write pixel changes to Unity’s internal copy of the texture then uploads it to the texture. I believe that can be any kind of texture including rendertextures. It’s not exactly fast, though… sending data over the graphics bus to the GPU is very slow in Unity compared to other engines, partly due to having to first copy data into an internal texture and then to send that texture over the graphics bus in its entirety.

If you need performance a rendertexture wont help you here at all, they are only helpful when the GPU is rendering straight to the rendertexture, bypassing the cpu entirely. TextureHelper is using the cpu to modify the texture in main memory and then sending it to the GPU, so performance isn’t anywhere near what it would be straight from the GPU.

To can improve performance by splitting your large texture into smaller textures and arranging them in a grid to cover the screen, then only uploading the textures which actually have changes on them in a given frame, or limiting the number of uploads per frame in a queue. But you then have to beware of a large number of draw calls because every texture causes a new draw call.

Have you written your own JPG export or is the one converted from actionscript (Google Code Archive - Long-term storage for Google Code Project Hosting.)?

I have used a jpeg saving code which was converted to Unity javascript… I believe from actionscript… that’s why the asset includes a copyright notice required for redistribution of that code. JPEG file processing is quite advanced (for me), I wouldn’t be able to code my own routines.

I purchased this for the Ellipse functionality, but it doesn’t seem to be working. This is what I get:

The bottom left corner looks like it’s kinda correct, but the rest is broken. Any idea on what could be happening?

its a bit odd. it should work. i’ll check the code tonight.

Hmm well the code seems ok, it works for me, it works in the demo scene. Can you tell me a) what size your texture is and the format, and b) what numbers you’re giving to the function?

    function TextureEllipse(Tex:Texture2D, XCenter:int, YCenter:int, XRadius:int, YRadius:int, WithColor:Color, ApplyChanges:boolean) {
        //Draw a filled axis-aligned ellipse within a Texture using the given color
        //Centered at XCenter,YCenter with radii of XRadius and YRadius
        //Based on mid-point ellipse algorithm, almost entirely integer math
         //Pixels will only upload to the actual texture if ApplyChanges=true
        if (Tex) {
             var Wide:int=Tex.width;                        //Width in pixels (row length)
             var High:int=Tex.height;                    //Height in pixels
             var p:int;
             var px:int;
             var py:int;
             var x:int;
             var y:int;
             var prevy:int;
            var Rx2:int;
            var Ry2:int;
            var twoRx2:int;
            var twoRy2:int;
            var pFloat:float;
            Rx2=XRadius*XRadius;
            Ry2=YRadius*YRadius;
            twoRx2=Rx2*2;
            twoRy2=Ry2*2;
            //Region 1
            x=0;
            y=YRadius;
            TextureRect(Tex,XCenter-XRadius,YCenter,(XCenter-XRadius)+(XRadius*2),YCenter,WithColor,false);
            pFloat=(Ry2-(Rx2*YRadius))+(0.25*Rx2);
            p=pFloat + (Mathf.Sign(pFloat)*0.5);
            px=0;
            py=twoRx2*y;
            while (px<py-1) {
                prevy=y;
                x+=1;
                px+=twoRy2;
                if (p>=0) {
                    y-=1;
                    py-=twoRx2;
                }
                if (p<0) {
                    p+=Ry2+px;
                } else {
                    p+=(Ry2+px-py);
                }
                if ((y<prevy) && (px<py-1)) {
                    TextureRect(Tex,XCenter-x,YCenter+y,(XCenter-x)+(x*2),YCenter+y,WithColor,false);
                    TextureRect(Tex,XCenter-x,YCenter-y,(XCenter-x)+(x*2),YCenter-y,WithColor,false);
                }
            }
            //Region 2
            pFloat=(Ry2*(x+0.5)*(x+0.5))+(Rx2*(y-1.0)*(y-1.0))-(Rx2*(Ry2));
            p=pFloat + (Mathf.Sign(pFloat)*0.5);
            y+=1;
            while (y>1) {
                y-=1;
                py-=twoRx2;
                if (p<=0) {
                    x+=1;
                    px+=twoRy2;
                }
                if (p>0) {
                    p+=(Rx2-py);
                } else {
                    p+=(Rx2-py+px);
                }
                TextureRect(Tex,XCenter-x,YCenter+y,(XCenter-x)+(x*2),YCenter+y,WithColor,false);
                TextureRect(Tex,XCenter-x,YCenter-y,(XCenter-x)+(x*2),YCenter-y,WithColor,false);
            }
             if (ApplyChanges==true) {Tex.Apply();}        //Apply the changes
        }
    }

Oh, weird. It works now… Sorry. Thanks!