Difference between revisions of "User:LindaB Helendale/BMPbase64"

From Second Life Wiki
Jump to navigation Jump to search
(Created page with "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 with scripts, or visualizi…")
 
Line 2: Line 2:


The following script is a working demo. To use the functions copy the segment from
The following script is a working demo. To use the functions copy the segment from
<lsl>
//======= BMP image routines ===============================================
//======= BMP image routines ===============================================
</lsl>
to  
to  
<lsl>
//=== end of BMP image routines ======================
//=== end of BMP image routines ======================
</lsl>
into the script.
into the script.




----
<lsl>
//======= BMP image routines ===============================================
//======= BMP image routines ===============================================
//
//
Line 202: Line 208:
     }
     }
}
}
</lsl>

Revision as of 10:39, 24 January 2012

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 with scripts, or visualizing some land attributes. The following script functions print out html code for a base64 encoded BMP image. The script output in chat can be copied to a text file, and the file can be opened as an image with any web browser. From the web browser the image can be saved in BMP format to upload in SL or to use in any image manipulation applications.

The following script is a working demo. To use the functions copy the segment from <lsl> //======= BMP image routines =============================================== </lsl> to <lsl> //=== end of BMP image routines ====================== </lsl> into the script.



<lsl> //======= 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();
   }

} </lsl>