Texture meta-data

From Second Life Wiki
Revision as of 13:31, 17 July 2009 by Which Linden (Talk | contribs)

Jump to: navigation, search

On upload, j2c textures will have all CME segments in the main header removed to be replaced by a single CME segment. That segment will be serialized as an unordered key-value url query string with the following keys and associated values.

The agent_id of the resident that uploaded the texture.
The utc time the texture was uploaded in YYYYmmddHHMMSS format.
The original width of the image as reported by the viewer. This value must be interpretable as an integer greater than 0 and less than 1,000,000.
The original height of the image as reported by the viewer. This value must be interpretable as an integer greater than 0 and less than 1,000,000.
The average color of the image as calculated during verification. The value will be RRGGBBAA in hex with 00 being none and ff representing full saturation of that component. If the image only has three components, then alpha is set to ff. If there are more than four components, the extra components are skipped.


The actual encoded comment string will look something like:


tool support

The j2c_comments.py script understands j2c codestream CME markers and will parse for them in the main header. To use it, pass a list of j2c codestream file names on the command line. The script will emit the file name parsed and report on comments found for each. If the example above is in test.texture, the session would look something like:

$ ./j2c_comments.py test.texture
test.texture: {

The tool will emit error messages if the codestream could not be understood, or if the comment is not latin1. Comments which do not conform to the query string format will be treated as a plain, raw latin1 string.



  1. !/usr/bin/env python

""" Extract out all of the j2c CME markers """

import cgi import optparse import struct

class NotJ2C(Exception):


def parse_cme(filename):

   j2c = file(filename, 'rb')
   comments = None
   while True:
       marker_bytes = j2c.read(2)
       marker, marker_type = struct.unpack('BB', marker_bytes)
       if marker != 0xff:
           raise NotJ2C
       if marker_type == 0x90:
           # we found the SOT, so we're done.
           return comments
       elif marker_type == 0x4f:
           # this is SOC, which does not have a lsoc element
       size_bytes = j2c.read(2)
       size = struct.unpack('!H', size_bytes)[0] - 2
       if marker_type == 0x64:
           # found a CME. read it, and interpret it.
           rcme = j2c.read(2)
           rcme = struct.unpack('!H', rcme)[0]
           if rcme == 0:
               new_comment = '<binary>'
           elif rcme == 1:
               raw_comment = j2c.read(size - 2)
               new_comment = cgi.parse_qs(raw_comment)
               if len(new_comment) == 0:
                   new_comment = raw_comment
               new_comment = '<unknown>'
           if comments is None:
               comments = new_comment
           elif type(comments) == 'list':
               comments = [comments, new_comments]
       j2c.seek(size, 1)

def main():

   parser = optparse.OptionParser(
       usage="usage: %prog [options] file_list",
       description="Read through file list and extract the CME field")
   options, args = parser.parse_args()
   if len(args) < 1:
       parser.error("Please specify one or more files to assign.")
   for filename in args:
           comments = parse_cme(filename)
           print filename + ': ' + str(comments)
       except NotJ2C:
           print filename + ': Unable to parse file'

if __name__ == "__main__":