最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to sychronously get the width and height from a base64 string of image data? - Stack Overflow

programmeradmin1浏览0评论

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.

Share Improve this question edited Dec 14, 2016 at 19:21 1.21 gigawatts asked Dec 14, 2016 at 19:16 1.21 gigawatts1.21 gigawatts 17.9k40 gold badges145 silver badges273 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 3

This 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 and readFloat 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;
}
发布评论

评论列表(0)

  1. 暂无评论