Byte[] >> Texture?

hellllo,

please, help me with that coding stuff…

currently i’m stuck with that:

i have a byte Object and want to turn it into a texture.

basically the same thing like in the Template (EX9.Texture), but instead of a fill function a byte object.

at the moment, i have this workaround:

byte[]() tileObj = (byte[]())command.ExecuteScalar(); // what i get from a sqlite DB

if (tileObj == null)
{
	//here i want to put an "empty" texture in my texture
	
}
else
{
	// here i managed to put the byte[]() into a BitmapImage
	BitmapImage image = new BitmapImage();
	image.BeginInit();
	image.CacheOption = BitmapCacheOption.OnLoad;
	image.UriSource = null;
	image.StreamSource = new MemoryStream[byte[](](https://vvvv.org/documentation/byte[]()tileObj);
	image.EndInit();
	
	// then convert the BitmapImage into a Bitmap and put it into the textures metadata
	info.TileBM = BitmapImage2Bitmap(image) ;
}

a function that converts BitmapImage into Bitmap

private Bitmap BitmapImage2Bitmap(BitmapImage bitmapImage)
{
			
	using(MemoryStream outStream = new MemoryStream())
	{
		BitmapEncoder enc = new BmpBitmapEncoder();
		enc.Frames.Add(BitmapFrame.Create(bitmapImage));
		enc.Save(outStream);
		System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(outStream);
				
		// return bitmap; <-- leads to problems, stream is closed/closing ...
		return new Bitmap(bitmap);
	}
}

that’s how i push the Bitmap in my texture

unsafe void UpdateTexture(Info info, Texture texture)
{
	TextureUtils.CopyBitmapToTexture(info.TileBM, texture);
}

an extra question:
how to easily create a new “empty” texture with specific dimensions. (empty: RGBA = 0)


you can find the code and patch https://discourse.vvvv.org/

that’s it - thank you for reading that far

suppose the byte array contains only pixel after pixel in RGBA format (no header), something like this should work:

// somewhere in evaluate
info.Width = from sqlite; // width in pixel
info.Height = from sqlite; // height in pixel
info.Data = from sqlite; // byte[]()

...

// in update texture
var dataRectangle = texture.Lock(0, LockFlags.None);
try
{
  // compute pitch of source and destination (might differ, as size of textures are often a power of two, depends on hardware)
  var srcPitch = info.Data.Length / info.Height;
  var dstPitch = dataRectangle.Pitch;
  var dstDataStream = dataRectangle.Data;

  if (srcPitch == dstPitch)
  {
    // pitch is the same, we can do the copy in one pass
    dstDataStream.WriteRange(info.Data);
  }
  else
  {
    // pitch differs, we need to copy line by line
    for (int i = 0; i < height; i++)
    {
      var srcOffset = srcPitch * i;
      var dstOffset = dstPitch * i;
      dstDataStream.Position = dstOffset;
      dstDataStream.WriteRange(info.Data, srcOffset, srcPitch);
    }
  }
}
finally
{
  texture.UnlockRectangle(0);
}

hi elias… and thanks, will try that asap.

Can you please explain why that’s faster (is that the simplest or best implementation?).

And, how could i’ve figured that out by myself?

thanks

and, i get an error in this line:

var dataRectangle = texture.Lock(0, LockFlags.None);

SlimDX.Direct3D9.Texture" enthält keine Definition für “Lock”, und es konnte keine Erweiterungsmethode “Lock” gefunden werden, die ein erstes Argument vom Typ “SlimDX.Direct3D9.Texture” akzeptiert (Fehlt eine Using-Direktive oder ein Assemblyverweis?).

doesn’t look like a big one, but i don’t know which usings or assembly references i need.

once again:

info.Tilesize = width = height = 256

well then the bytes in your byte array are clearly in a different format.

suppose it’s really a bitmap (like saved to disk as *.bmp), which probably has some header and other stuff in it:

byte[]() tileObj = (byte[]())command.ExecuteScalar(); // what i get from a sqlite DB

using (var stream = new MemoryStream(tileObj))
{
  var decoder = BitmapDecoder.Create(stream, BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.None);
                
  BitmapSource bitmapSource = decoder.Frames[0](0);
  if (bitmapSource.Format != PixelFormats.Bgra32)
  {
    bitmapSource = new FormatConvertedBitmap(bitmapSource, PixelFormats.Bgra32, decoder.Palette, 0.0);
  }
  info.BitmapSource = bitmapSource;
}

...

// in update texture
var dataRectangle = texture.Lock(0, LockFlags.None);
try
{
  var dstPitch = dataRectangle.Pitch;
  var dstDataStream = dataRectangle.Data;
  var dstBuffer = data.DataPointer;

  info.BitmapSource.CopyPixels(Int32Rect.Empty, dstDataStream.DataPointer, (int)dstDataStream.Length, dstPitch);
}
finally
{
  texture.UnlockRectangle(0);
}

oh and pls don’t post compile errors. i didn’t test this code, just wrote it down as i thought it should work. at least it does in another project when reading *.bmp

Out of interest, what format your texture comes from?

Have you simply tried:
Texture.FromMemory(device, array); ?

thanks for the answers!

unfortunately i didn’t manage to implement any of your suggestions…

azenos latest idea seems to be the right direction but leads to exceptions like

Auf das verworfene Objekt kann nicht zugegriffen werden. ≃ the discarded object can not be accessed.

what seems to be caused by CopyPixels()

@vux
the images are PNGs inside a sqlite database…

earlier, i did this:

FOutput[i](i) = System.Text.Encoding.GetEncoding(1252).GetString[byte[](](https://vvvv.org/documentation/byte[]()tileObj);

what worked with a DynamicTexture (String)

well probably the easiest way to get it going is changing the BitmapCacheOptions when creating the decoder. right now it’s set to None -> it will try to access the stream provided when creating the decoder. since we disposed the stream already (using (var stream = …)) you get that object disposed exception when reading the pixels.

other possibility would be to save the already decoded memory stream in your info class. so you’d decode (call CopyPixels) in evaluate and only copy the data in update texture.

png is not a bitmap, but compressed, no?

yes, i also thought that png is kind of a compressed bitmap … but then i simply tried

FOutput[i](i) = System.Text.Encoding.GetEncoding(1252).GetString[byte[](](https://vvvv.org/documentation/byte[]()tileObj);

and got correct data for the dynamic texture (as stated above). i thought, that this would have been impossible, if the data was compressed in any kind?

so, perhaps the data is in pure bitmap format, but has a .png file extension?

PNG Specification: Rationale

see also:

PNG Encoding Compression &
PNG Compression

i’d guess so. the first 8 characters of a png should already mess up your output, since they’d be interpreted as pixels as well. maybe try to cache it to disk to analyse with more control?

ok, sherlock sebl was investigating:

as i said, i have this sqlite database containing the images as “blobs”.

i’ve exported one tile as png and .txt. perhaps one of you guys knows how to convert that data.

one more thing:

in tilemill (the tool that creates the database) i can choose the image format: png(24-bit), png(8-bit) and jpegs in various qualities

perhaps, jpegs are easier/faster in my case?

containing png and txt (256.9 kB)

ok, so you didn’t tried my one line working solution? :)

just tested your file:

doing:

byte[]() data = System.IO.File.ReadAllBytes("path to your file.txt");

then

Texture t = Texture.FromMemory(device,data);

works :)

hey vux,

i tried, but failed…

can u please explain how to modify the Template (EX9.Texture) to fit your solution? (e.g where to put this line?)

thanks

edit - got it:

Texture CreateTexture(Info info, Device device)
		{
			FLogger.Log(LogType.Debug, "Creating new texture at slice: " + info.Slice);
			
			return Texture.FromMemory(device , info.Bytes);
		}

wooohooo

but i can’t do that in updateTexture … there’s no device available)

ah, finally :)

if you need to do it in update:

using (var surface = texture.GetSurfaceLevel(0))
{
  Surface.FromFileInMemory(surface, info.Bytes, Filter.None, 0);
}

i’m not quite sure about the using though.