RE: PNG to Base64 string
(12-27-2018 08:23 AM)Didier Lachieze Wrote: Here is how I would do it :
Code:
L0:={137,80,78}; L1:={};
N:=L0(1)*256^2+L0(2)*256+L0(3);
FOR I FROM 3 DOWNTO 0 DO
L1(0):=IP(N/64^I);
N:=N-L1(0)*64^I;
END;
L1 will return {34,21,1,14}, but you can also do the lookup and build the Base64 string directly in the loop rather than building L1.
Interesting, this makes sense. I don't work with binary enough to have figured that one out easily. I implemented this method and compared performance to original version, there is a significant improvement. Using a fairly typical screen resulting in a ~26KB file; my times were:
- Previously posted version: ~14.8s
- Previous version without Step 5: ~14.7s
- New version using Didier's method: ~5.2s
I experimented reading 3 bytes at a time with AFilesB, but that proved to be much slower than reading in the maximum number of bytes into a local variable, then processing it 3 bytes per iteration.
Here's the latest version:
Code:
WriteBase64();
Base64String();
EXPORT base64img2()
BEGIN
LOCAL fname:="b64img2.html",htmlcontent,fsize,fiter,cur_chunk,j;
AFiles(fname):=G0;
htmlcontent:="<!doctype html><html lang=\"en\"><head><meta charset=\"utf-8\"><title>Base64 Screen Capture</title></head><body><div><img style=\"display:block; margin:0 auto;\" src=\"data:image/png;base64,"+WriteBase64(fname)+"\" alt=\"Sample Screen Capture\" /></div></body></html>";
DelAFiles(fname);
fsize:=DIM(htmlcontent);
IF fsize>10000 THEN // max of 10000 items in a list
fiter:=IP(fsize/10000);
FOR j FROM 1 TO fiter DO
cur_chunk:=ASC(MID(htmlcontent,(j-1)*10000+1,10000));
AFilesB(fname,(j-1)*10000):=cur_chunk;
END;
IF FP(fsize/10000) THEN
cur_chunk:=ASC(MID(htmlcontent,fiter*10000+1));
AFilesB(fname,fiter*10000):=cur_chunk;
END;
ELSE
AFilesB(fname,0):=ASC(htmlcontent);
END;
END;
WriteBase64(fname)
BEGIN
LOCAL fcontent,j,b64str:="",fsize,fiter;
fsize:=AFilesB(fname);
IF fsize>9998 THEN // lists can not be longer than 10000, 9999 divides by 3 evenly
fiter:=IP(fsize/9999);
FOR j FROM 1 TO fiter DO
fcontent:=AFilesB(fname,(j-1)*9999,9999);
b64str:=b64str+Base64String(9999,fcontent);
END;
IF FP(fsize/9999) THEN
fcontent:=AFilesB(fname,fiter*9999,fsize-(fiter*9999));
b64str:=b64str+Base64String(SIZE(fcontent),fcontent);
END;
ELSE
fcontent:=AFilesB(fname,0,fsize);
b64str:=b64str+Base64String(SIZE(fcontent),fcontent);
END;
RETURN b64str;
END;
Base64String(fsize,mylst)
BEGIN
LOCAL fiter:=IP(fsize/3),L1,N,j,i,mystr:="";
LOCAL dict:={"A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f","g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v","w","x","y","z","0","1","2","3","4","5","6","7","8","9","+","/"};
FOR j FROM 1 TO fiter DO
N:=mylst(j*3-2)*256^2+mylst(j*3-1)*256+mylst(j*3);
FOR i FROM 3 DOWNTO 0 DO
L1:=IP(N/64^i);
mystr:=mystr+dict(L1+1);
N:=N-L1*64^i;
END;
END;
IF fsize-fiter*3 THEN // fractional
IF (fsize-fiter*3)==1 THEN
N:=mylst(fsize)*256^2;
FOR i FROM 3 DOWNTO 2 DO
L1:=IP(N/64^i);
mystr:=mystr+dict(L1+1);
N:=N-L1*64^i;
END;
mystr:=mystr+"==";
ELSE // must be 2
N:=mylst(fsize-1)*256^2+mylst(fsize)*256;
FOR i FROM 3 DOWNTO 1 DO
L1:=IP(N/64^i);
mystr:=mystr+dict(L1+1);
N:=N-L1*64^i;
END;
mystr:=mystr+"=";
END;
END;
RETURN mystr;
END;
EDIT: Removed duplicate <body> tag
|