Is there a way to get an image width and height from a base64 encoded image data synchronously? Possibly by header information.
I must make this extremely clear. I'm not interested in async methods. Please move on if you want to know reasons why async won't work. Don't even ment.
Here is my example code to encode my data. Encoding is synchronous. I'm using this call:
var base64ImageData:String = DisplayObjectUtils.getBase64ImageDataString(displayObject, DisplayObjectUtils.PNG, null, true);
Here is my code to decode my data. The only method I've found in AS3 to decode is asynchronous. I'm using this call:
var bitmapData:BitmapData = DisplayObjectUtils.getBitmapDataFromBase64(bitmapDataString);
Let me repeat, I'm looking for a synchronous action. It cannot be asynchronous.
More information:
I'm working in ActionScript3 which is a sibling to JavaScript. If it works in JavaScript it most likely works in AS3. But I cannot work with the dom. No adding images to the dom and checking values.
Helpful hacks:
If it's possible to add width and height into the base64 stream and then get it later that will work as well.
Is there a way to get an image width and height from a base64 encoded image data synchronously? Possibly by header information.
I must make this extremely clear. I'm not interested in async methods. Please move on if you want to know reasons why async won't work. Don't even ment.
Here is my example code to encode my data. Encoding is synchronous. I'm using this call:
var base64ImageData:String = DisplayObjectUtils.getBase64ImageDataString(displayObject, DisplayObjectUtils.PNG, null, true);
Here is my code to decode my data. The only method I've found in AS3 to decode is asynchronous. I'm using this call:
var bitmapData:BitmapData = DisplayObjectUtils.getBitmapDataFromBase64(bitmapDataString);
Let me repeat, I'm looking for a synchronous action. It cannot be asynchronous.
More information:
I'm working in ActionScript3 which is a sibling to JavaScript. If it works in JavaScript it most likely works in AS3. But I cannot work with the dom. No adding images to the dom and checking values.
Helpful hacks:
If it's possible to add width and height into the base64 stream and then get it later that will work as well.
3 Answers
Reset to default 3This only works for png's first thing first is to convert base64 do a buffer (Uint8array) then read the byte 16-20 (width) and 20-24 (height) as int32 value
function getPngDimensions(base64) {
let header = base64.slice(0, 50)
let uint8 = Uint8Array.from(atob(header), c => c.charCodeAt(0))
let dataView = new DataView(uint8.buffer, 0, 28)
return {
width: dataView.getInt32(16),
height: dataView.getInt32(20)
}
}
// Just to get some random base64 images
var random = (bottom, top) => Math.floor( Math.random() * ( 1 + top - bottom ) ) + bottom
var canvas = document.createElement('canvas')
canvas.width = random(10, 100)
canvas.height = random(10, 100)
console.log(`canvas width: ${canvas.width}, height: ${canvas.height}`)
var base64 = canvas.toDataURL().split(',')[1]
console.log(base64)
var dimensions = getPngDimensions(base64)
console.log(dimensions)
The following does not work but it's attempting to convert @Endless answer to AS3. I have this so far:
protected function windowedapplication1_applicationCompleteHandler(event:FlexEvent):void {
var base64ImageData:String = DisplayObjectUtils.getBase64ImageDataString(this, DisplayObjectUtils.PNG, null, true);
var size:Object = getPngDimensions(base64ImageData);
}
public function getPngDimensions(base64:String):Point {
base64 = base64.split(',')[1];
trace(base64.length);
base64 = base64.replace(/\n/g, "");
trace(base64.length);
//var header:String = base64.slice(0, 52); // 50 gave error later on
var header:String = base64.slice(0, base64.length);
var base64Decoder:Base64Decoder = new Base64Decoder();
var byteArray:ByteArray;
base64Decoder.reset();
base64Decoder.decode(header);
// base64Decoder.toByteArray(); ==>
// Error: A partial block (2 of 4 bytes) was dropped. Decoded data is probably truncated!
// added the whole length (52 works as well)
byteArray = base64Decoder.toByteArray();
byteArray.position = 0;
//var width:String = byteArray.readUTFBytes(0);
//var value:ByteArray = new ByteArray();
//value.readBytes(byteArray, 0, 28);
var thing:Number = byteArray.readDouble(); // -8.091055181950927E-264
byteArray.position = 0;
var thing2:Number = byteArray.readFloat(); // -2.5073895107184266E-33
byteArray.position = 0;
var width:Number = byteArray.readFloat();
byteArray.position = 16;
var width:Number = byteArray.readFloat();
byteArray.position = 20;
var width:Number = byteArray.readFloat();
byteArray.position = 28;
var width:Number = byteArray.readFloat(); // 1.1074230549312416E-38
byteArray.position = 0;
var other:String = byteArray.readUTFBytes(byteArray.length);
// other == "PNG\r\n\n"
var point:Point = new Point();
point.x = int(width);
point.y = int(height);
return point;
}
If I use ByteArray.readUTFBytes()
I get "PNG" as a result but no width or height.
"If I use
ByteArray.readUTFBytes()
I get "PNG" as a result but no width or height."
Don't read bytes as UTF
format since that only extracts text chars. You want decimal values.
readDouble
andreadFloat
is too much, they read 8 bytes per action. PNG format uses only 4 bytes per width OR height value, so just read (unsigned) integers.- Remember that every
read
action automatically moves the byteArray position. For example to know bytePos-1's value you must "walk"/read past it so you end up at bytePos-2, or for Double type you will end up at bytePos-9.
See if this re-fix of your code gets the expected values of width & height...
public function getPngDimensions(base64:String):Point
{
base64 = base64.split(',')[1];
trace("Base64 length A : " + base64.length);
base64 = base64.replace(/\n/g, "");
trace("Base64 length B : " + base64.length);
//var header:String = base64.slice(0, 52); // 50 gave error later on
var header:String = base64.slice(0, base64.length);
var base64Decoder:Base64Decoder = new Base64Decoder();
var byteArray:ByteArray;
base64Decoder.reset();
base64Decoder.decode(header);
// base64Decoder.toByteArray(); ==>
// Error: A partial block (2 of 4 bytes) was dropped. Decoded data is probably truncated!
// added the whole length (52 works as well)
byteArray = base64Decoder.toByteArray();
byteArray.position = 0;
//# Get Width and Height values
//# type Int is 4 bytes long so pointer auto-moves ahead by 4 bytes during Read action
byteArray.position = 16; //start position for values...
var width:int = byteArray.readUnsignedInt(); //reads 4 bytes, stops at Pos 20
var height:int = byteArray.readUnsignedInt(); //reads 4 bytes, stops at Pos 24
//# Double check (if needed)
//trace("PNG width : " + width);
//trace("PNG height : " + height);
//# Return as Point
var point:Point = new Point();
point.x = width; point.y = height;
return point;
}