User:LindaB Helendale/BMPbase64

From Second Life Wiki
< User:LindaB Helendale
Revision as of 05:01, 7 September 2015 by LindaB Helendale (talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Create BMP image in-world

Introduction

Creating textures in-world is not possible in SL, but for some tasks it is useful to be able to produce images with scripts. Such as making sculpt maps, or visualizing some land attributes. The following script functions print out html code for a base64 encoded BMP image.

Usage

Copy and paste the script output from chat to a text file and save as .htm or .html file on the computer. You don't need to remove other chat lines that may have occurred while the script prints the image. After saving the file, open it with any web browser. The image can be saved in BMP format from the web browser to upload in SL or to use in any image manipulation applications.


The following script is a working demo. To use the functions in your script copy the segment from

//======= BMP image routines ===============================================

to

 
//=== end of BMP image routines ======================

into the script.


Main Script

 
//======= BMP image routines ===============================================
//
// (c) LindaB Helendale, permission to use the routines freely is granted.
//     Please retain the copyright statement
//
// These routines print html code for showing a base64 encoded BMP image.
// Copy and paste the chat between <html> and </html> to a file, and save,
// and open with web browser. You can save it as BMP in the browser to upload in SL.
//
// Usage:  
//  BMP_open_stream(width, heigth)    - opens the BMP data stream and prints the header
//    The pixels are added with the following functions. The rows run from bottom to top
//    and columns from left to rigth.
//      BMP_add_pixel_color(colorVec) - adds pixel defined by color vector to the stream
//      BMP_add_pixel_RGB(R,G,B)      - adds pixel defined by integers R G B to the stream 
//  BMP_close_stream()                - closes the stream 


// Output line length can be about 1000 in SL, 
// but take care that the application where you paste the data doesn't add line breaks 
integer MAX_OUTPUT_LINE_LENGTH = 500; 

string HTMLheader=
"------ Copy to a file below this line ---------

<html><body><img id='image'><script language='JavaScript'>
var data = 'data:image/bmp;base64,";

string HTMLtail=
"var s_img = document.getElementById('image');
s_img.src = data; 
</script></body></html>

------ Copy to a file above this line ---------";

// to scale the image, you can add the following js commands in the tail before </script>
//  s_img.setAttribute('width', 512); s_img.setAttribute('height',512);

// header of the BMP image, byte per byte
list BMPheader=[66,77, 54,48,0,0, 0,0,0,0, 54,0,0,0, 40,0,0,0, 32,0,0,0, 128,0,0,0, 1,0, 24,0, 0,0,0,0, 0,48,0,0, 194,30,0,0, 194,30,0,0, 0,0,0,0, 0,0,0,0];

list swap_bytes(integer J) {
  return [ J & 0xff , (J>>8) & 0xff, (J>>16) &  0xff, (J>>24) & 0xff];
}

string BMP_encoded_header(integer width, integer heigth) {
    // return encoded BMP header for the given size image, setting width, height, image size and file size
    string encodedHeader="";
    list headerBytes=BMPheader;
    integer imSize = heigth * width * 3;
    integer fileSize = imSize + 54; 
    headerBytes = BMPheader;
    headerBytes = llListReplaceList(headerBytes, swap_bytes(fileSize),2,5);
    headerBytes = llListReplaceList(headerBytes, swap_bytes(width),18,21);
    headerBytes = llListReplaceList(headerBytes, swap_bytes(heigth),22,25);
    headerBytes = llListReplaceList(headerBytes, swap_bytes(imSize),34,37);
    integer p;
    for(p=0;p<54;p+=3) {
        integer x = ((llList2Integer(headerBytes,p) & 0xff) << 24 ) |
                    ((llList2Integer(headerBytes,p+1) & 0xff) << 16 ) |
                    ((llList2Integer(headerBytes,p+2) & 0xff) << 8 ) ;
        string s=llGetSubString(llIntegerToBase64(x),0,3);
        encodedHeader += s;
    }
    return encodedHeader;
}
    
integer DATA_SIZE;  // the required number of data items (3 byte/4 char) in the stream
string  LINEBUFF;   // output buffer
integer DATA_LINES; // the number of printed data lines, used for printing the js code for merging the data
integer DATA_ITEMS; // the number of printed data items, used for error checking

BMP_open_stream(integer width, integer heigth) {
    DATA_SIZE = width*heigth;
    DATA_LINES=0;
    DATA_ITEMS=0;
    LINEBUFF="";
    string bmpheader=BMP_encoded_header(width,heigth);
    llOwnerSay("\n\n" + HTMLheader + bmpheader + "';\n/*");
}

BMP_close_stream() {            
    if (LINEBUFF != "") {
        llOwnerSay("\n*/\ndata" + (string)DATA_LINES + " = '" + LINEBUFF + "';\n/*");
        LINEBUFF=="";
        DATA_LINES++;
    }
    // print the javascript code to append the data<line> variables to the data stream
    integer i;
    LINEBUFF = "\n*/\ndata+=";
    for(i=0;i<DATA_LINES;i++) {
        string s =  "data" + (string)i  + "+";
        if (llStringLength(LINEBUFF) + llStringLength(s) + llStringLength(HTMLtail) > 1000) {
            llOwnerSay(llGetSubString(LINEBUFF,0,-2) + ";\n/*");
            LINEBUFF =  "\n*/\ndata+=";
        }
        LINEBUFF += s;
        if ((i+1) % 24 == 0) {
            LINEBUFF = llGetSubString(LINEBUFF,0,-2) + ";\ndata+=";
        }
    }
    llOwnerSay(llGetSubString(LINEBUFF,0,-2) + ";\n" + HTMLtail + "\n");
    
    if (DATA_ITEMS != DATA_SIZE) {
        llOwnerSay("\nWarning: the size of the image is " + (string)DATA_SIZE +
                   " pixels. \The number of pixels written is " + (string)DATA_ITEMS);
    }
}

BMP_add_pixel_color(vector rgb) {
    // Add pixel specified by color vector to the stream
    rgb *= 255; 
    integer x = ((integer)rgb.x<<8) | ((integer)rgb.y<<16) | ((integer)rgb.z<<24);
    string s=llGetSubString(llIntegerToBase64(x),0,3);
    BMP_add_3bytes_encoded(s);
}
    
BMP_add_pixel_RGB(integer R, integer G, integer B) {
    // Add pixel specified by three integer RGB values to the stream
    integer x = (R<<8) | (G<<16) | (B<<24);
    string s=llGetSubString(llIntegerToBase64(x),0,3);
    BMP_add_3bytes_encoded(s);
}
    
BMP_add_3bytes_encoded(string s) {
    // print the data in the stream, with new line if needed
    if (llStringLength(LINEBUFF) + llStringLength(s) > MAX_OUTPUT_LINE_LENGTH) {
        llOwnerSay("\n*/\ndata" + (string)DATA_LINES + "='" + LINEBUFF + "';\n/*");
        LINEBUFF=s;
        DATA_LINES++;
    }else{
        LINEBUFF += s;
    }
    DATA_ITEMS++;
}
//=== end of BMP image routines ======================
    
// this is needed for the demo pic only
vector hsv2rgb(vector hsv) {
    float h=hsv.x;
    float s=hsv.y;
    float v=hsv.z;
    h = 6*h;
    integer k = llFloor(h-6*0.000001); 
    if (k<0) { k=0;}
    float f = h-k;
    float t = 1-s;
    float n = 1-s*f;
    float p = 1-(s*(1-f));
    float r = (k==0)   + (k==1)*n + (k==2)*t + (k==3)*t + (k==4)*p + (k==5);
    float g = (k==0)*p + (k==1)   + (k==2)   + (k==3)*n + (k==4)*t + (k==5)*t;
    float b = (k==0)*t + (k==1)*t + (k==2)*p + (k==3)   + (k==4)   + (k==5)*n;
    float max=r; if (g>max) max=g; if (b>max) max=b;
    f = v/max;
    vector rgb=f*<r,g,b>;
    return(rgb);
}

  
default
{
    // make test image:  64 x 12 image with
    //  2 pixel pink stripe on top
    //  8 pixel HSV chart in middle, with saturation growing upwards
    //  2 pixel lilac stripe below
    // in BMP the starting row is at bottom, columns run from left ro right
        
    touch_start(integer p) {
        if (llDetectedKey(0)!=llGetOwner()) return;
        integer R=12;
        integer C=64;
        BMP_open_stream(C,R);
        
        integer row; integer col;
        for(row=0;row<R;row++) {
            for(col=0;col<C;col++) {
                vector rgb;
                if (row<=1) { 
                    // lilac stripe
                    rgb = <100,0,255>/255.0;
                }else if (row>=10) {
                    // pink stripe
                    rgb = <255,170,230>/255.0;
                }else{
                    // HSV chart
                    vector hsv=<(float)col / (float)(C), (float)(row-2) / 7.01 , 1>;
                    rgb=hsv2rgb(hsv);
                }
                BMP_add_pixel_color(rgb);
            }
        }
        BMP_close_stream();
    }
}