lainchan / λ
An encoder/decoder for converting files into images and vice versa written in JavaScript
Junichi Fujiwara · 2019-06-30 23:58
I wrote a tool to convert images into files and files to images (including UTF-8 text) for imageboards and image-sharing websites. Although, I don't know a lot about this community but I just wanted to share this here instead of 4chan.
Try it online: https://jsfiddle.net/hasbdw8g/
Sabo Engle · 2019-07-01 00:56
I haven't looked at your code yet because using single-letter variable names is unhelpful, but from your image you appear to be wasting a quarter of your pixel array on an opaque alpha channel that only changes to transparent for the unused pixels of the last row. If you put either the payload size or the padding size into the pixel array you can dump the alpha channel.
λx.x · 2019-07-01 05:42
This seems like an appropriate thread for your topic:
>>10399
Once you acknowledge this or a few days pass, I'll be merging this thread with that one, so you'll be able to continue the discussion there. You should read the Lainchan rules page and the sticky for this board if you haven't already. Lainchan is slower than other imageboards and threads live for a long while.
Lastly, it's not kind to require someone to execute JavaScript to take a look at your program. If you read the sticky, you'll learn that you can give your file a txt extension and then upload it like you would any other file.
Yu-ichi · 2019-07-01 10:05
>>14089
Yes, I thought it was kinda a mistake to make an entire thread just for this, so you should definitely merge. At least JavaScript is "sandboxed" in the web browser so it can't be maliciously harmful to the computer except for excessive lagging, unless if you try really hard that solely revolves around that intention.
Here's a screenshot of it.
>>14083
I just had an idea of using divisors (or factors) for the width that wraps the pixels instead, so to avoid adding another pixel that indicates the size of the previous pixel's beside it. Or you could just make it a straight line which is hard to point and click on by assigning the width with the value of total pixels (horizontal) or just 1 (vertical), but I like it better as a rectangle.
Zero Cool · 2019-07-01 10:17
I found this algorithm, tabs aren't formatted correctly so I converted them to spaces:
function listDivisors(n){
var small=[],
large=[],
end=Math.floor(Math.sqrt(n));
for(var i=1;i<=end;i++){
if(n%i===0){
small.push(i);
if(i*i!==n){
large.push(n/i);
}
}
}
large.reverse();
return small.concat(large);
}
hash table · 2019-07-01 10:36
>>14093
Then you can combine this by:
var findClosest=(array,n)=>array.length?array.reduce((p,c)=>Math.abs(c-n)<Math.abs(p-n)?c:p):array[0];
var width=findClosest(listDivisors(total),Math.sqrt(total));
Batou · 2019-07-01 10:40
>>14091
None of that addresses the alpha channel space waste.
>I just had an idea of using divisors (or factors) for the width that wraps the pixels instead
You can be given a file with prime length. You can be given a file with length equal to the product of a very small prime and a large prime.
>but I like it better as a rectangle.
Then padding is unavoidable in the general case.
You also have an error in your decode function. You do not handle the case where none of your LAST_PIXEL markers are found in an arbitrary input image, and the computation proceeds with uninitialized 'r' and 'n' variables. It is also curious that you are using const but not let.
HELIOS · 2019-07-01 10:54
>>14095
To fix that, you have to make comparisons with the very last pixel on that image to see where the change occurs and use that instead if there's no pixel that corresponds to LAST_PIXEL.
Yamaoto Toshi · 2019-07-01 11:01
>>14094
Computing all divisors to get the one closest to the square root is wasteful. The closest one is the first one you find iterating down from the square root, at which point you do not require the others.
You are also searching for a value in a sorted list by linear scan, instead of binary search.
>>14096
The last pixel is already tested in the current version.
Tank · 2019-07-01 11:02
>>14095
ES2015's let would've been used if it had something to do with objects, asynchronous iterators and timers.
let · 2019-07-01 11:22
>>14098
You can read up on the benefits of let over var in any number of places, such as:
https://medium.com/javascript-scene/javascript-es6-var-let-or-const-ba58b8dcde75
https://ardalis.com/javascript-let-const-and-var
↑ · 2019-07-01 16:09
>>14095
>None of that addresses the alpha channel space waste.
Could it be used to implement some form of Reed-Solomon code?
pop · 2019-07-01 20:22
>>14102
You can put whatever metainformation you want into your packing scheme, such as the error correction code of your choice. At that point though you are probably better off using some standard archive format and packing that into the pixel array without further complications. Additionally, PNG itself already includes checksums for every chunk, and thus for all IDAT chunks, so a corrupted file is extremely unlikely to be read as a valid PNG and will signal an error before you even get to the pixel array.
Lord Nikon · 2019-07-01 20:36
>>14103
Yeah, but error correction in this case wouldn't be trying to preserve the PNG, but the data encoded to it. For example, a compressed image is still valid as a PNG file, however it would not only be invalid as an encoded EXE through OP's algorithm, but also it could be downright dangerous.
aliased · 2019-07-01 22:45
>>14104
>wouldn't be trying to preserve the PNG, but the data encoded to it
As already stated, if the PNG becomes invalid the PNG reader will raise an error for a failed chunk checksum and you won't even get to the pixel array. See https://github.com/lainchan/lainchan/issues/111 for an example.
>a compressed image is still valid as a PNG file
All PNG files use deflate compression.
Cutter · 2019-07-02 00:01
>>14081
Very cool Lainon.
That could be useful.
Tank · 2019-07-02 22:53
Here is a quick and simplistic but deobfuscated decoder for OP's current packing scheme.
>>> import re
>>> def readfile (path):
... with open (path, 'rb') as file:
... return file.read ()
...
>>> def writefile (path, data):
... with open (path, 'wb') as file:
... file.write (data)
...
>>> def decode (data):
... matchobj = re.search (b'\xff(\x00\x00|\xff\x00|\xff\xff)\xff(\x00{4})*$', data)
... if matchobj is None:
... raise ValueError ("no end marker")
... end = matchobj.start ()
... count_ff = sum (data [end : end + 3]) // 0xff
... payload_size = len (data) // 4 * 3 - (3 - count_ff)
... mutable = bytearray (data)
... del mutable [3::4]
... del mutable [payload_size:]
... return mutable
...
>>> writefile ('payload.out', decode (readfile ('pixels.raw')))
>>> To get the pixel array:
$ convert encoded.png rgba:pixels.raw If the payload is known to be plain ASCII text, like in the OP image, a far simpler method will suffice:
$ tr -d '\377\000' < pixels.raw > payload.txt