I am stuck with a fairly serious problem. I'm rewriting Flash to JS and ran into a problem with SOAP returned data handling. It is about images that I get as string and I convert to BitmapData used in Flash. I've tried many different approaches, but at best it gets a green image with noise on the canvas. Below are all the code snippets you may find useful
Code from Flash:
private function encode(bitmap:Bitmap):ByteArray{
var encoder:JPGEncoder = new JPGEncoder(QUALITY);
return encoder.encode(bitmap.bitmapData);
}
public function decodeBytes(bm:Bitmap):void{
_bitmap = bm;
_bytesData = encode(_bitmap);
var imgConventer:ArrayDataConventer = new ArrayDataConventer();
imgConventer.addEventListener(ImageConvertCompleteEvent.IMAGE_CONVERT_COMPLETE, convertCompleteHandler);
imgConventer.decByteArrToHexStr(bytesData);
}
decByteArrToHexStr return string where two hex chars represent byte. And this string is pushed to SOAP and its returned when I want to get it. So this is Flash part.
Now I want this string convert to image data I can put into canvas.
I have method to convert string to Uint8Array.
public hexStrToDecByteArr(str: string): Uint8Array {
const byteArr: Uint8Array = new Uint8Array(str.length / 2);
for (let i: number = 0; i < str.length; i = i 2) {
const n: number = parseInt('0x' str.substr(i, 2), 0);
if (!isNaN(n)) {
byteArr[i] = n;
}
}
return data;
}
And then in response handler I have something like this:
const decodes: ArrayDataConverter = new ArrayDataConverter();
const data = decodes.hexStrToDecByteArr(downloadedImage.sData);
const encoder: any = new JPGEncoder(100);
const encoded = encoder.encode({width: 400, height: 300, data});
const context = this.canvas.nativeElement.getContext('2d');
context.clearRect(0, 0, 400, 300);
const image = new Image();
image.onload = () => {
context.drawImage(image, 0, 0);
};
image.src = encoded;
So downloadedImage.sData contains hex string. JPGEncoder is package I found which is JPGEncoder Flash version rewritten to JS (https://www.npmjs.com/package/@koba04/jpeg-encoder).
As I mentioned earlier I'm getting green image with some noises on canvas.
Thanks in advance.
CodePudding user response:
It looks like you're compressing actual pixel colours (raw bitmap data) with JPEG compression and then trying to load same JPEG data into HTML5 canvas?
This way won't work because Canvas expects pixel colour values not JPEG compression algorithm.
Possible fix #1: Using AS3 pixel values into Canvas...
In your AS3 code, do not encode into JPEG but instead just write the pixel values as hex string (from Array).
public function decodeBytes(bm:Bitmap):void
{
//# get pixel values into an array named: _bytesData
bm.bitmapData.copyPixelsToByteArray( bm.bitmapData.rect, _bytesData );
var imgConventer:ArrayDataConventer = new ArrayDataConventer();
imgConventer.addEventListener(ImageConvertCompleteEvent.IMAGE_CONVERT_COMPLETE, convertCompleteHandler);
imgConventer.decByteArrToHexStr( _bytesData );
}
Then on the TypeScript (or JavaScript) side just read the received data into an array/buffer.
To display the image in (HTML5) Canvas you must use a for-loop to read through the array
var canvas = document.getElementById('myCanvas');
var ctx = canvas.getContext('2d');
var imgData = ctx.createImageData( canvas.width, canvas.height );
//# notice the order is:
//# JS imagedata = R-G-B-A
//# AS3 bitmap = A-R-G-B
for (var i=0; i < imgData.data.length; i = 4)
{
imgData.data[i 0] = downloadedImage.sData[i 1]; //put RED
imgData.data[i 1] = downloadedImage.sData[i 2]; //put GREEN
imgData.data[i 2] = downloadedImage.sData[i 3]; //put BLUE
imgData.data[i 3] = downloadedImage.sData[i 0]; //put ALPHA
}
//# Update canvas with the image data
ctx.putImageData(imgData,0,0);
This should show your image in the Canvas (which has id
of myCanvas
) on your web page.
Possible fix #2: Using JPEG data in array...
Since the above method of sending raw bitmap data could mean you're sending a large file, you could try using JPEG compression anyway and this way means you can load your JPEG array directly into an <Image>
tag just like a loaded file.
There is no need to encode JPEG twice (first in AS3 then again secondly in TypeScript, this is just corrupting your image data)
(1) First create the JPEG as you do in your original AS3 code, then (2) On the TypeScript or HTML side, check first 3 values of what is given to you (using alert
or console.log
options)...
downloadedImage.sData[0] //should be: 0xFF (or 255)
downloadedImage.sData[1] //should be: 0xD8 (or 216)
downloadedImage.sData[2] //should be: 0xFF (or 255)
Those 3 values are the correct and expected beginning bytes of a JPEG header (it shows that your data is really a working JPEG image).
In your HTML's <body>
section make sure you have an <image>
tag there waiting for the bytes image to be added as .src
...
<body>
<img id="myPic" src="none.jpg" width="600" height="400">
</body>
Then in JavaScript (I don't know TypeScript so you will convert this part by yourself)
<script>
//# downloadedImage.sData String must look like "0xFF,0xD8,0xFF" etc (using 0xFF or 0xff is same result)
var myArr = new Uint8Array( [ downloadedImage.sData ] );
var blob = new Blob( [downloadedImage.sData], { type: "image/jpeg" } );
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL( blob );
var img = document.getElementById( "myPic" );
img.src = imageUrl;
</script>
Hopefully this is useful to getting a working result.
PS: Some complete testable example code (HTML/JS only, no TypeScript)...
<!DOCTYPE html>
<html>
<body>
<img id="myPic" src="none.jpg" width="600" height="400">
</body>
<script>
//# fill the array properly & fully, below is just small example of expected values
var myArr = new Uint8Array( [0xFF,0xD8,0xFF,0xE0,0x00 ... 0xFF,0xD9]);
var blob = new Blob( [myArr], { type: "image/jpeg" } );
var urlCreator = window.URL || window.webkitURL;
var imageUrl = urlCreator.createObjectURL( blob );
var img = document.getElementById( "myPic" );
img.src = imageUrl;
</script>
</html>