translating hex values from ByteArray in AS3 - actionscript-3

I'm new to using ByteArray, and am trying to parse hex color values from an .ase (Adobe Swatch Exchange) using ByteArray in AS3. I can see position of the hex values using a hex editor, but don't know which methods to use to parse the hex value from there. Here are the values copied from the hex editor. The 2 color values are #ffffff and #cdcdcd:
ASEF........¿..........$...#.f.f.f.f.f.f..RGB.?Ä..?Ä..?Ä....¿..........$...#.c.d.0.0.c.d..RGB.?MÕŒ....?MÕŒ
I've made a weak initial attempt to get the first color, but am stuck here :
var byteA:ByteArray = _fr.data;
byteA.position=29;
var tb:ByteArray = new ByteArray()
var tA:Array= new Array(); //temp array
byteA.readBytes(tb,0,11);
trace("TB "+ tb[0]+":" +tb.toString())
Can somebody please show me how to parse out the color value, so it can be added to the temp array tA? As a bonus answer, since there can be multiple colors in a swatch, advice on a method to parse out all colors in a given .ase file would be greatly appreciated. Thanks in advance for helping me through this!

The ASEF defines colors using RGB, assuming each color is 8 bits (called a byte). That's 24 bits of information. Unfortunately, Flash's ByteArray doesn't have a method that reads just 24 bits. So, we'll read each byte individually and combine them later.
var byteArray:ByteArray = _fr.data;
//Skip past the garbage that isn't the color.
for (var index:int = 0; index < numberOfBytesUntilFirstColor)
{
byteArray.readByte();
}
var redValue:int = byteArray.readByte();
var greenValue:int = byteArray.readByte();
var blueValue:int = byteArray.readByte();
I'll let you calculate numberOfBytesUntilFirstColor as I'm not familiar with ASE file format.
If you want to store the value as an RGB integer, usable by Flash, do this:
var color:int = redValue;
color = color << 8;
color = color | greenValue;
color = color << 8;
color = color | blueValue;
The above code combines the individual color bytes into one 32 bit int (note, the high 8 bits are left as 0. Sometimes Flash uses the high 8 bits for the alpha, sometimes not.)

Related

How to inverse font color based on cell's background Color in Google Apps Script?

I'm wondering how to inverse the font color of a cell's value based on the background color automatically to make it readable.
I've used the following script to get the background color automatically set upon pasting hex color codes into given cells How do I change a cell to the color of the hexadecimal value of a cell in Google Spreadsheets?, with sample sheet here
function onEdit(e) {
r = e.range;
if(r.getSheet().getSheetName() == "colors"){ //the sheet I want to apply this to is called colors
var rows = r.getNumRows();
var columns = r.getNumColumns();
var colors = [] //this is our 2 dimensional array of colors in case you copy and paste a large amount of colors
for (var i = 1; i <= rows; i++) { //go down each row
var row = [] //create a new row array to clear the old one when we go down a row
for (var j = 1; j <= columns; j++) { //then go across each column
row.push(r.getCell(i,j).getValue()) //put together the row of colors
}
colors.push(row); //insert our row of colors so we can go down the next row
}
r.setBackgrounds(colors) //batch update in case you update many colors in one copy and paste otherwise it will be very slow
}
}
but the remaining issue is the font is not showing on the dark background cells.
I've also found this related question How to decide font color in white or black depending on background color?.
And those Javascript specific answers with functions, but I've not been able to make them work with the above script in GAS.
JavaScript code 1
JavaScript code 2
JavaScript code 3
JavaScript code 4
JavaScript code 5
JavaScript code 6
I've also looked into the documentation for setFontColors(colors) and saw we could use the method r.setFontColors(colors) in the script above.
I tried calling the JavaScript codes 1 to 6 above, but I'm not succeeding.
For example, I've tried this way based on JavaScript code 3 ):
function onEdit(e) {
r = e.range;
if(r.getSheet().getSheetName() == "colors"){ //the sheet I want to apply this to is called colors
var rows = r.getNumRows();
var columns = r.getNumColumns();
var colors = [] //this is our 2 dimensional array of colors in case you copy and paste a large amount of colors
for (var i = 1; i <= rows; i++) { //go down each row
var row = [] //create a new row array to clear the old one when we go down a row
for (var j = 1; j <= columns; j++) { //then go across each column
row.push(r.getCell(i,j).getValue()) //put together the row of colors
}
colors.push(row); //insert our row of colors so we can go down the next row
}
r.setBackgrounds(colors) //batch update in case you update many colors in one copy and paste otherwise it will be very slow
r.setFontColors(lum([111, 22, 255]));
}
}
function lum(rgb) {
var lrgb = [];
rgb.forEach(function(c) {
c = c / 255.0;
if (c <= 0.03928) {
c = c / 12.92;
} else {
c = Math.pow((c + 0.055) / 1.055, 2.4);
}
lrgb.push(c);
});
var lum = 0.2126 * lrgb[0] + 0.7152 * lrgb[1] + 0.0722 * lrgb[2];
return (lum > 0.179) ? '#000000' : '#ffffff';
}
What am I missing?
Thanks for your insights!
I believe your goal is as follows.
When the hex values are put to the cells, you want to set the background color using the inputted hex values. At that time, you want to set the font colors for each cell depending on the background colors.
Modification points:
In your script, I think that the value of colors can be retrieved by r.getValues(). In this case, the for loop is not required to be used.
From r.setFontColors(lum([111, 22, 255]));, When you want to set the same font colors to the range, you can modify as follows.
From
r.setFontColors(lum([111, 22, 255]));
To
r.setFontColor(lum([111, 22, 255]));
But, from your script, I thought that you might want to set the font colors by each background color. If my understanding is correct, how about the following modification?
Modified script:
// I modified this function.
function onEdit(e) {
var r = e.range;
if (r.getSheet().getSheetName() == "colors") {
var colors = r.getValues();
r.setBackgrounds(colors);
var fonrColors = r.getBackgroundObjects().map(r => r.map(c => {
var obj = c.asRgbColor();
return lum([obj.getRed(), obj.getGreen(), obj.getBlue()]);
}))
r.setFontColors(fonrColors);
}
}
// This is from your script.
function lum(rgb) {
var lrgb = [];
rgb.forEach(function (c) {
c = c / 255.0;
if (c <= 0.03928) {
c = c / 12.92;
} else {
c = Math.pow((c + 0.055) / 1.055, 2.4);
}
lrgb.push(c);
});
var lum = 0.2126 * lrgb[0] + 0.7152 * lrgb[1] + 0.0722 * lrgb[2];
return (lum > 0.179) ? '#000000' : '#ffffff';
}
In this modification, in order to convert the hex to RGB, I used the method of getBackgroundObjects(). Of course, you can convert this using Javascript. If you don't want to use getBackgroundObjects(), please check this thread
In this modification, when you put the hex values to the cells, the background colors are set using the hex values, and the font colors are set depending on the background colors using the function lum of your script.
References:
getBackgroundObjects()
Related thread.
RGB to hex and hex to RGB
Required parameter of setFontColors is a 2d array
Try to change r.setFontColors(lum([111, 22, 255])); into the following codes:
const fontColor = lum([111, 22, 255]);
const fontColorsRow = new Array(columns).fill(fontColor);
const fontColors = new Array(rows).fill(fontColorsRow);
r.setFontColors(fontColors);
Reference:
setFontColors(colors)
Short Answer
To flip the text color between black or white based on the background color, You need to convert the background color to the luminance, and choose a specific luminance value to flip, typically about 0.37 Y works well.
Longer Answer
The reality is there are a number of psychophysical factors that affect the best point to flip from black or white text, and they are not all about the specific color. Font size and weight, and the surrounding colors, and the ambient lighting in the room all affect the "ideal flip point."
I discuss this in another answer on overflow with simple code snippets. After that answer, I also created a CodePen with live examples you can play with.
The "super simple down and dirty" is:
// First convert your sRGB values to luminance:
let sY = Math.pow(sR/255.0,2.2) * 0.2126 +
Math.pow(sG/255.0,2.2) * 0.7152 +
Math.pow(sB/255.0,2.2) * 0.0722; // Andy's Easy Luminance for sRGB. For Rec709 HDTV change the 2.2 to 2.4
// AND THEN SET THE TEXT COLOR PER:
let textColor = (sY < 0.37) ? "#fff" : "#000"; // Low budget down and dirty text flipper.
That's the minimum, simplest bit of code that should be easy to port. I'm not familiar with Google script, but basically you just need to divide each 8 bit color by 255.0, then apply 2.2 exponent, then multiply by the coefficient (0.2126 for red, 0.7152 for green, 0.0722 for blue) and sum together.
Flip at about 0.4 - the flippable range is about 0.36 to 0.43 ish, depending.
And if you really want to dive into the nitty gritty world of contrast of text for readability, check out the APCA and SAPC — at the SAPC dev site click on "research mode" to play with interactive experiments that demonstrate the concepts. These are new methods for determining contrast on self illuminated displays.

AS3 Check colour code similar?

Can you check how similar a code is to another colour, If e.g. 0xFFFFFF is similar to 0xFBFBFB then change colour.
im not sure how the colour code works, so if you know a tip on the code, can you please share
A simple method to determinate proximity between colors, is to do so for each color component.
In AS3, a color is a number containing 3 components : red, green and blue (There's sometimes a fourth component for transparency - alpha)
Each component is contained in 1 byte, for a value of 0 to 255. (0x00 to 0xFF)
Here is a way to get each color component from the color itself :
var red:int = (color & 0xFF0000) >>> 16;
var green:int = (color & 0xFF00) >>> 8;
var blue:int = color & 0xFF;
Once you have the color components of each color, you can compare them 2 by 2, and combine the results:
var red_proximity = Math.abs(red1 - red2);
var green_proximity = Math.abs(green1 - green2);
var blue_proximity = Math.abs(blue1 - blue2);
var color_proximity = red_proximity + green_proximity + blue_proximity;
The result will be a number between 0 and 765; 0 meaning the color are exactly the same, and 765 meaning that they are completely differents.
(Note that I am using Math.abs() to ensure that the proximity values are always positive.)
The last step now is to choose a threshold value to determine if the colors are "too close". The chosen value is usually arbitrary, so test a few and take your pick. For the example, I'm using the value '84'.
if (color_proximity < 84) {
// color is too close : change it
} else {
// color is okay : do nothing ?
}
This is only one method, and if you have to play a lot with colors, search a bit on the web for other algorithm; It's always usefull.

How do I get the hexadecimal value from a Color object in Actionscript 3.0?

So I am making a music visualizer and I am trying to set the linestyle for my graphics to be a pre defined color! But I get an error when trying
var lineColor:Color = new Color(120,120,12);
graphics.lineStyle(lineThickness, lineColor);
1067: Implicit coercion of a value of type fl.motion:Color to an unrelated type uint.
So then I tried
var lineColor:Color = new Color(120,120,12);
graphics.lineStyle(lineThickness, lineColor.color);
But lineColor.color always returns 0! What do I do? I can't believe there is no built in API to take a color object and make it a compatible hexadecimal uint for graphics!
Any help would be much apprectiated!
Just use a method to return the hex value.
function get_uint_from_colour(red:int, green:int, blue:int) {
return red << 16 | green << 8 | blue;
}
So in your example:
graphics.lineStyle(lineThickness, get_uint_from_colour(120, 120, 12));
In response to your comment about needing to use a set of pre-defined Color objects, you will still need to (at some point) convert them to the type uint as this is the accepted data-type for the lineStyle method.
My suggestion would be to adjust the get_uint_from_colour method to accept a Color object. Like so:
function get_uint_from_colour(colour:Color) {
return int(colour.redMultiplier) << 16 | int(colour.greenMultiplier) << 8 | int(colour.blueMultiplier);
}
So in your code:
graphics.lineStyle(lineThickness, get_uint_from_colour(lineColor));
Please note that I have changed the name of my original method as I felt it better describes the function
Also, keep in mind that a Color object can hold much more information about itself than a standard Hex colour can - this superfluous information will be lost in the process of converting it.

Using byteArray with getPixel (no 's') then setPixelS

Here is what I am trying to do:
one byteArray (colorsByteArray) holds color values, that I created with the "getPixels" method;
for every pixel on screen, I make calculations and depending on that I get a rank that I use to retrieve the proper color value inside colorsByteArray;
finally, I write inside a second byteArray (pixelsByteArray) the color I just retrieved. Then I 'draw' this byteArray using the "setPixels" method.
Problem is I do not find the same image as if I simply wrote a "setPixel" for every pixel in my loop. The image I get is missing one line out of two, and is kind of repeated three times horizontally. Of course I guess I must be writing (or reading) something wrong from my second byteArray, but can someone explain me what I am doing wrong? Or even if my efforts are worth anything actually :-) cause I didn't find anything quite like this so far...
Thanks!
Here is a bit of my code to make things clearer:
colorsByteArray = bitmDext.getPixels(new Rectangle(0, 0, myShadeBMD.width, 1));
canvas.lock();
for(var i:int = 0; i < STAGE_WIDTH; i++)
{
for(var j:int = 0; j < STAGE_HEIGHT; j++)
{
//make some calculation on each pixel to get the 'colorRank' uint value
colorsByteArray.position = colorRank * 4;//unsigned int is 32 bytes, representing 4 slots of a byteArray
var colorValue:uint = colorsByteArray.readUnsignedInt();
//canvas.setPixel(i, j, colorValue);//works just fine
pixelsByteArray.writeUnsignedInt(colorValue);
}
}
pixelsByteArray.position = 0;
var myRect:Rectangle = new Rectangle(0, 0, STAGE_WIDTH, STAGE_HEIGHT);
canvas.setPixels(myRect, pixelsByteArray);
canvas.unlock();
added a picture that may help:
http://www.oghel.com/pictures/stackOverflow/example3
the expected part is on the right, the wrong one on the left.

Bitmap conversion - Creating a transparent + black image from a B&W source

I have a whole bunch of jpg files that I need to use in a project, that for one reason or another cannot be altered. Each file is similar (handwriting), black pen on white BG. However I need to use these assets against a non-white background in my flash project, so I'm trying to do some client-side processing to get rid of the backgrounds using getPixel and setPixel32.
The code I am currently using currently uses a linear comparison, and while it works, the results are less than expected, as the shades of grey are getting lost in the mix. Moreso than just tweaking my parameters to get things looking proper, I get the feeling that my method for computing the RGBa value is weak.
Can anyone recommend a better solution than what I'm using below? Much appreciated!
private function transparify(data:BitmapData) : Bitmap {
// Create a new BitmapData with transparency to return
var newData:BitmapData = new BitmapData(data.width, data.height, true);
var orig_color:uint;
var alpha:Number;
var percent:Number;
// Iterate through each pixel using nested for loop
for(var x:int = 0; x < data.width; x++){
for (var y:int = 0; y < data.height; y++){
orig_color = data.getPixel(x,y);
// percent is the opacity percentage, white should be 0,
// black would be 1, greys somewhere in the middle
percent = (0xFFFFFF - orig_color)/0xFFFFFF;
// To get the alpha value, I multiply 256 possible values by
// my percentage, which gets multiplied by 0xFFFFFF to fit in the right
// value for the alpha channel
alpha = Math.round(( percent )*256)*0xFFFFFF;
// Adding the alpha value to the original color should give me the same
// color with an alpha channel added
var newCol = orig_color+alpha;
newData.setPixel32(x,y,newCol);
}
}
var newImg:Bitmap = new Bitmap(newData);
return newImg;
}
Since it's a white background, blendMode may give you a better result.