- Home /
Split BMP into smaller Textures using Unity
I am currently trying to split a larger BMP (304x160) into smaller Bmp files (16x16) using C# script in unity. Unfortunately I cannot add the larger BMP file to the Project but must load it using System.IO as eventually this BMP file will be sent from a game server.
I've tried splitting the larger BMP into smaller BMPs using two methods:
using System.Drawing.Graphics.DrawImage()
using System.Drawing.Bitmap.Clone()
Unfortunately option #1 throws an Invalid Win32 GDI exception while Option #2 is throwing an "OutOfMemoryException".
The end goal is: I need to split this larger BMP (which holds 2d textures), store these Texture2Ds in an array, so that I can apply each texture to a cube (instantiated prefab). Which texture to use is specified by the game server depending on the location of the cube.
I'm currently at a loss as how to get around these issues while working within the Unity Framework. (Yes I know using System.Drawing isn't exactly in the framework but supoorted when I drag/dropped it into my Project).
Any suggestions are welcome!
Current Code attempts:
public static bool BitmapToArray(out System.Drawing.Bitmap[,] tiles, System.Drawing.Bitmap bmpfile, int dimX, int dimY, int padX, int padY)
{
int itemsInX = (bmpfile.Width - padX) / (dimX + padX);
int itemsInY = (bmpfile.Height - padY) / (dimY + padY);
tiles = new Bitmap[itemsInX, itemsInY];
for (int i = 0; i < itemsInY; i++)
{
for (int j = 0; j < itemsInX; j++)
{
//below caused errors
//System.Drawing.Bitmap smallBMP = new System.Drawing.Bitmap(dimX, dimY);
//Graphics g = Graphics.FromImage(smallBMP);
//g.DrawImage (bmpfile, 0, 0, new Rectangle((j*dimX)+ padX,(i*dimY)+padY, dimX, dimY), GraphicsUnit.Pixel);
//g.Dispose();
//tiles[j,i] = smallBMP;
tiles[j,i] = bmpfile.Clone(new Rectangle((j*dimX)+ padX,(i*dimY)+padY, dimX, dimY),System.Drawing.Imaging.PixelFormat.Format32bppArgb);
}
}
return true;
}
Update:
I'm still stuck on this issue. It turns out that my code works fine for Bitmaps in a non-indexed format. However, unfortunately, it does not work for Indexed formats, such as the Format8bppIndexed Pixelformat that I have receiving from the game server.
I've even tried using System.Drawing.Bitmap.LockBits and accessing the pixel data directly. Unfortunately with Unity this does not work and throws errors.
I'm still stuck trying to access the pixel information from the Format8bppIndexed bitmap, so that I can split it and convert it into a Texture2d for use in Unity.
Any suggestions at all on how to proceed???
Answer by iwaldrop · Dec 24, 2013 at 11:09 PM
You don't need to break them up at all. Just adjust the UV coordinates of the cube's material instance to the location on the texture. This has the added benefit of allowing all of your blocks to be dynamically batched for fewer drawcalls, which in-turn allows higher frame rates.
Checkout the material page in the Unity Docs for info about texture offsets. To discover the offsets you can manually set play with them in the inspector and evaluate which offsets each type of block should have.
Unfortunately when i attempt to do this I run into an issue trying to just apply the format8bppIndexed bmp to a Texture. This process works with 32 bit formats of Bmps but not the 8 bit Indexed. I assume this is because internally it is trying to use getPixel / setPixel.
For example the following code that I tried to do fails on the .save(). Note that tileset is my format8bppIndexed bitmap.
Texture2D tileTexture = new Texture2D(tileSet.Width,tileSet.Height);
using ($$anonymous$$emoryStream ms = new $$anonymous$$emoryStream())
{
tileSet.Save (ms, System.Drawing.Imaging.ImageFormat.Png);
//tileSet.Save (ms, System.Drawing.Imaging.ImageFormat.Bmp);
tileTexture.LoadImage (ms.ToArray());
}
$$anonymous$$gestion on how to proceed in this route? I'm all for trying anything that works!
Like so:
using UnityEngine;
using System.Collections;
public class TextureOffsetter : $$anonymous$$onoBehaviour
{
public Texture2D texture;
public Vector2 offset;
public Vector2 scale;
void Awake()
{
renderer.material.mainTexture = texture;
renderer.material.mainTextureScale = scale;
renderer.material.mainTextureOffset = offset;
}
}
Again, focus your attention on the $$anonymous$$aterial's mainTextureOffset and mainTextureScale properties.
For a single texture that has 4 sub-images, set your scale to 0.5 for both x and y, and to change them alternate combinations of 0 and 0.5 for offset x and y. Obviously you can fit many images into the same texture, just to the math:
1 / {number of textures} = scale
Thanks for the information iwaldrop. I've started using the texture offset ins$$anonymous$$d of splitting the bmps. Has made my life a little easier.
It wasn't my main issue in the end, the problem was the way I was loading the B$$anonymous$$P from the file, creating a $$anonymous$$emoryStream to save it but then disposing of the $$anonymous$$emoryStream. Later on in the code when i tried to access the B$$anonymous$$P all GDI+ activities (.save, .lockbits, etc) all failed. From my tinkering around for ages and a chance 'mistake' (forgetting to dispose of the memorystream for a test load that ended up in everything working), I was pointed to the .dispose actually being the problem. It had nothing to do with the bitmap pixelformat (regardless of if the B$$anonymous$$P in the file was format8bppIndexed or format16Rgb or even format32rgb). Turns out that the memorystream becomes attached to the created Bit$$anonymous$$ap when creating it from that memorystream. As such, you shouldn't dispose it, as disposing of the Bit$$anonymous$$ap later will take care of that.
$$anonymous$$ore information on that is here (for any others that come across this post): http://stackoverflow.com/questions/336387/image-save-throws-a-gdi-exception-because-the-memory-stream-is-closed
Your answer pointed me in the right direction so I've accepted it! Thanks for the help.
Your answer
![](https://koobas.hobune.stream/wayback/20220613124041im_/https://answers.unity.com/themes/thub/images/avi.jpg)