Fixed "clear()" and added "clearto(r, g, b)".

Added class RandomVinFile which supports random access and warming the cache.
Added eofseen and errorseen methods to BasicVinFile.
Use RGB mode for rgb8 data on entry level Indigo.
Minor cosmetic changes.
This commit is contained in:
Guido van Rossum 1992-09-07 15:28:57 +00:00
parent 2de9b68fea
commit 4526f379bc

View file

@ -11,6 +11,9 @@
# BasicVoutFile: write a CMIF video file # BasicVoutFile: write a CMIF video file
# VinFile: BasicVinFile + Displayer # VinFile: BasicVinFile + Displayer
# VoutFile: BasicVoutFile + Displayer + Grabber # VoutFile: BasicVoutFile + Displayer + Grabber
#
# XXX Future extension:
# BasicVinoutFile: supports overwriting of individual frames
# Imported modules # Imported modules
@ -25,6 +28,7 @@
Error = 'VFile.Error' # file format errors Error = 'VFile.Error' # file format errors
CallError = 'VFile.CallError' # bad call CallError = 'VFile.CallError' # bad call
AssertError = 'VFile.AssertError' # internal malfunction
# Constants returned by gl.getdisplaymode(), from <gl/get.h> # Constants returned by gl.getdisplaymode(), from <gl/get.h>
@ -75,6 +79,61 @@ def choose_conversion(format):
raise Error, 'Unknown color system: ' + `format` raise Error, 'Unknown color system: ' + `format`
# Inverses of the above
def inv_grey(r, g, b):
y, i, q = colorsys.rgb_to_yiq(r, g, b)
return y, 0, 0
def inv_yiq(r, g, b):
y, i, q = colorsys.rgb_to_yiq(r, g, b)
return y, i/1.2 + 0.5, q + 0.5
def inv_hls(r, g, b):
h, l, s = colorsys.rgb_to_hls(r, g, b)
return l, h, s
def inv_hsv(r, g, b):
h, s, v = colorsys.rgb_to_hsv(r, g, b)
return v, h, s
def inv_rgb(r, g, b):
raise Error, 'Attempt to invert RGB colormap'
def inv_rgb8(r, g, b):
r = int(r*7.0)
g = int(g*7.0)
b = int(b*7.0)
rgb = ((r&7) << 5) | ((b&3) << 3) | (g&7)
return rgb / 255.0, 0, 0
# Choose one of the above based upon a color system name
def choose_inverse(format):
try:
return eval('inv_' + format)
except:
raise Error, 'Unknown color system: ' + `format`
# Predicate to see whether this is an entry level (non-XS) Indigo.
# If so we can lrectwrite 8-bit wide pixels into a window in RGB mode
def is_entry_indigo():
# XXX hack, hack. We should call gl.gversion() but that doesn't
# exist in earlier Python versions. Therefore we check the number
# of bitplanes *and* the size of the monitor.
xmax = gl.getgdesc(GL.GD_XPMAX)
if xmax <> 1024: return 0
ymax = gl.getgdesc(GL.GD_YPMAX)
if ymax != 768: return 0
r = gl.getgdesc(GL.GD_BITS_NORM_SNG_RED)
g = gl.getgdesc(GL.GD_BITS_NORM_SNG_GREEN)
b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE)
return (r, g, b) == (3, 3, 2)
# Routines to grab data, per color system (only a few really supported). # Routines to grab data, per color system (only a few really supported).
# (These functions are used via eval with a constructed argument!) # (These functions are used via eval with a constructed argument!)
@ -90,14 +149,10 @@ def grab_rgb8(w, h, pf):
raise Error, 'Sorry, can only grab rgb8 in single-buf rgbmode' raise Error, 'Sorry, can only grab rgb8 in single-buf rgbmode'
if pf <> 1 and pf <> 0: if pf <> 1 and pf <> 0:
raise Error, 'Sorry, can only grab rgb8 with packfactor 1' raise Error, 'Sorry, can only grab rgb8 with packfactor 1'
r = gl.getgdesc(GL.GD_BITS_NORM_SNG_RED) if not is_entry_indigo():
g = gl.getgdesc(GL.GD_BITS_NORM_SNG_GREEN) raise Error, 'Sorry, can only grab rgb8 on entry level Indigo'
b = gl.getgdesc(GL.GD_BITS_NORM_SNG_BLUE)
if (r, g, b) <> (3, 3, 2):
raise Error, 'Sorry, can only grab rgb8 on 8-bit Indigo'
# XXX Dirty Dirty here. # XXX Dirty Dirty here.
# XXX Set buffer to cmap mode, grab image and set it back. # XXX Set buffer to cmap mode, grab image and set it back.
# XXX (Shouldn't be necessary???)
gl.cmode() gl.cmode()
gl.gconfig() gl.gconfig()
gl.pixmode(GL.PM_SIZE, 8) gl.pixmode(GL.PM_SIZE, 8)
@ -193,7 +248,7 @@ def printinfo(self):
# Class to display video frames in a window. # Class to display video frames in a window.
# It is the caller's responsibility to ensure that the correct window # It is the caller's responsibility to ensure that the correct window
# is current when using showframe(), initcolormap() and clear() # is current when using showframe(), initcolormap(), clear() and clearto()
class Displayer(VideoParams): class Displayer(VideoParams):
@ -211,6 +266,8 @@ def init(self):
# Internal flags # Internal flags
self.colormapinited = 0 # must initialize window self.colormapinited = 0 # must initialize window
self.skipchrom = 0 # don't skip chrominance data self.skipchrom = 0 # don't skip chrominance data
self.color0 = None # magic, used by clearto()
self.fixcolor0 = 0 # don't need to fix color0
return self return self
# setinfo() must reset some internal flags # setinfo() must reset some internal flags
@ -219,12 +276,17 @@ def setinfo(self, values):
VideoParams.setinfo(values) VideoParams.setinfo(values)
self.colormapinited = 0 self.colormapinited = 0
self.skipchrom = 0 self.skipchrom = 0
self.color0 = None
self.fixcolor0 = 0
# Show one frame, initializing the window if necessary # Show one frame, initializing the window if necessary
def showframe(self, data, chromdata): def showframe(self, data, chromdata):
if not self.colormapinited: if not self.colormapinited:
self.initcolormap() self.initcolormap()
if self.fixcolor0:
gl.mapcolor(self.color0)
self.fixcolor0 = 0
w, h, pf = self.width, self.height, self.packfactor w, h, pf = self.width, self.height, self.packfactor
factor = self.magnify factor = self.magnify
if pf: factor = factor * pf if pf: factor = factor * pf
@ -247,18 +309,28 @@ def showframe(self, data, chromdata):
gl.rectzoom(factor, factor) gl.rectzoom(factor, factor)
gl.lrectwrite(self.xorigin, self.yorigin, \ gl.lrectwrite(self.xorigin, self.yorigin, \
self.xorigin + w - 1, self.yorigin + h - 1, data) self.xorigin + w - 1, self.yorigin + h - 1, data)
gl.gflush()
# Initialize the window: set RGB or colormap mode as required, # Initialize the window: set RGB or colormap mode as required,
# fill in the colormap, and clear the window # fill in the colormap, and clear the window
def initcolormap(self): def initcolormap(self):
self.colormapinited = 1
self.color0 = None
self.fixcolor0 = 0
if self.format == 'rgb': if self.format == 'rgb':
gl.RGBmode() gl.RGBmode()
gl.gconfig() gl.gconfig()
self.colormapinited = 1
gl.RGBcolor(200, 200, 200) # XXX rather light grey gl.RGBcolor(200, 200, 200) # XXX rather light grey
gl.clear() gl.clear()
return return
if self.format == 'rgb8' and is_entry_indigo():
gl.RGBmode()
gl.gconfig()
gl.RGBcolor(200, 200, 200) # XXX rather light grey
gl.clear()
gl.pixmode(GL.PM_SIZE, 8)
return
gl.cmode() gl.cmode()
gl.gconfig() gl.gconfig()
self.skipchrom = 0 self.skipchrom = 0
@ -269,29 +341,51 @@ def initcolormap(self):
if not self.quiet: if not self.quiet:
sys.stderr.write('Initializing color map...') sys.stderr.write('Initializing color map...')
self._initcmap() self._initcmap()
self.colormapinited = 1 gl.clear()
self.clear()
if not self.quiet: if not self.quiet:
sys.stderr.write(' Done.\n') sys.stderr.write(' Done.\n')
# Clear the window # Clear the window to a default color
def clear(self): def clear(self):
if not self.colormapinited: raise CallError if not self.colormapinited: raise CallError
if self.offset == 0: if gl.getdisplaymode() in (DMRGB, DMRGBDOUBLE):
gl.color(0x800) gl.RGBcolor(200, 200, 200) # XXX rather light grey
gl.clear()
else:
gl.clear() gl.clear()
return
gl.writemask(0xffffffff)
gl.clear()
# Do the hard work for initializing the colormap # Clear the window to a given RGB color.
# This may steal the first color index used; the next call to
# showframe() will restore the intended mapping for that index
def clearto(self, r, g, b):
if not self.colormapinited: raise CallError
if gl.getdisplaymode() in (DMRGB, DMRGBDOUBLE):
gl.RGBcolor(r, g, b)
gl.clear()
return
index = self.color0[0]
self.fixcolor0 = 1
gl.mapcolor(index, r, g, b)
gl.writemask(0xffffffff)
gl.clear()
gl.gflush()
# Do the hard work for initializing the colormap (internal).
# This also sets the current color to the first color index
# used -- the caller should never change this since it is used
# by clear() and clearto()
def _initcmap(self): def _initcmap(self):
convcolor = choose_conversion(self.format) convcolor = choose_conversion(self.format)
maxbits = gl.getgdesc(GL.GD_BITS_NORM_SNG_CMODE) maxbits = gl.getgdesc(GL.GD_BITS_NORM_SNG_CMODE)
if maxbits > 11: if maxbits > 11:
maxbits = 11 maxbits = 11
c0bits, c1bits, c2bits = self.c0bits, self.c1bits, self.c2bits c0bits = self.c0bits
c1bits = self.c1bits
c2bits = self.c2bits
if c0bits+c1bits+c2bits > maxbits: if c0bits+c1bits+c2bits > maxbits:
if self.fallback and c0bits < maxbits: if self.fallback and c0bits < maxbits:
# Cannot display frames in this mode, use grey # Cannot display frames in this mode, use grey
@ -310,10 +404,8 @@ def _initcmap(self):
offset = self.offset offset = self.offset
if maxbits <> 11: if maxbits <> 11:
offset = offset & ((1<<maxbits)-1) offset = offset & ((1<<maxbits)-1)
# XXX why is this here? self.color0 = None
# for i in range(512, MAXMAP): self.fixcolor0 = 0
# gl.mapcolor(i, 0, 0, 0)
# gl.gflush()
for c0 in range(maxc0): for c0 in range(maxc0):
c0v = c0/float(maxc0-1) c0v = c0/float(maxc0-1)
for c1 in range(maxc1): for c1 in range(maxc1):
@ -335,6 +427,11 @@ def _initcmap(self):
int(gv*255.0), \ int(gv*255.0), \
int(bv*255.0) int(bv*255.0)
gl.mapcolor(index, r, g, b) gl.mapcolor(index, r, g, b)
if self.color0 == None:
self.color0 = \
index, r, g, b
# Permanently make the first color index current
gl.color(self.color0[0])
gl.gflush() # send the colormap changes to the X server gl.gflush() # send the colormap changes to the X server
@ -379,6 +476,7 @@ def readfileheader(fp, filename):
filename + ': Unrecognized file header: ' + `line`[:20] filename + ': Unrecognized file header: ' + `line`[:20]
# #
# Get color encoding info # Get color encoding info
# (The format may change to 'rgb' later when packfactor == 0)
# #
if version <= 1.0: if version <= 1.0:
format = 'grey' format = 'grey'
@ -412,6 +510,7 @@ def readfileheader(fp, filename):
chrompack = 0 chrompack = 0
offset = 0 offset = 0
else: else:
# XXX ought to check that the format is valid
try: try:
c0bits, c1bits, c2bits, chrompack, offset = rest c0bits, c1bits, c2bits, chrompack, offset = rest
except: except:
@ -424,10 +523,13 @@ def readfileheader(fp, filename):
x = eval(line[:-1]) x = eval(line[:-1])
except: except:
raise Error, filename + ': Bad (w,h,pf) info' raise Error, filename + ': Bad (w,h,pf) info'
if type(x) <> type(()):
raise Error, filename + ': Bad (w,h,pf) info'
if len(x) == 3: if len(x) == 3:
width, height, packfactor = x width, height, packfactor = x
if packfactor == 0 and version < 3.0: if packfactor == 0 and version < 3.0:
format = 'rgb' format = 'rgb'
c0bits = 0
elif len(x) == 2 and version <= 1.0: elif len(x) == 2 and version <= 1.0:
width, height = x width, height = x
packfactor = 2 packfactor = 2
@ -548,6 +650,8 @@ def initfp(self, fp, filename):
filename + ': Bad version: ' + `self.version` filename + ': Bad version: ' + `self.version`
self.framecount = 0 self.framecount = 0
self.atframeheader = 1 self.atframeheader = 1
self.eofseen = 0
self.errorseen = 0
try: try:
self.startpos = self.fp.tell() self.startpos = self.fp.tell()
self.canseek = 1 self.canseek = 1
@ -578,9 +682,11 @@ def rewind(self):
self.fp.seek(self.startpos) self.fp.seek(self.startpos)
self.framecount = 0 self.framecount = 0
self.atframeheader = 1 self.atframeheader = 1
self.eofseen = 0
self.errorseen = 0
def warmcache(self): def warmcache(self):
pass print '[BasicVinFile.warmcache() not implemented]'
def printinfo(self): def printinfo(self):
print 'File: ', self.filename print 'File: ', self.filename
@ -598,24 +704,36 @@ def skipnextframe(self):
return t return t
def getnextframeheader(self): def getnextframeheader(self):
if self.eofseen: raise EOFError
if self.errorseen: raise CallError
if not self.atframeheader: raise CallError if not self.atframeheader: raise CallError
self.atframeheader = 0 self.atframeheader = 0
try: try:
return self._readframeheader(self.fp) return self._readframeheader(self.fp)
except Error, msg: except Error, msg:
self.errorseen = 1
# Patch up the error message # Patch up the error message
raise Error, self.filename + ': ' + msg raise Error, self.filename + ': ' + msg
except EOFError:
self.eofseen = 1
raise EOFError
def getnextframedata(self, ds, cs): def getnextframedata(self, ds, cs):
if self.eofseen: raise EOFError
if self.errorseen: raise CallError
if self.atframeheader: raise CallError if self.atframeheader: raise CallError
if ds: if ds:
data = self.fp.read(ds) data = self.fp.read(ds)
if len(data) < ds: raise EOFError if len(data) < ds:
self.eofseen = 1
raise EOFError
else: else:
data = '' data = ''
if cs: if cs:
cdata = self.fp.read(cs) cdata = self.fp.read(cs)
if len(cdata) < cs: raise EOFerror if len(cdata) < cs:
self.eofseen = 1
raise EOFError
else: else:
cdata = '' cdata = ''
self.atframeheader = 1 self.atframeheader = 1
@ -623,6 +741,8 @@ def getnextframedata(self, ds, cs):
return (data, cdata) return (data, cdata)
def skipnextframedata(self, ds, cs): def skipnextframedata(self, ds, cs):
if self.eofseen: raise EOFError
if self.errorseen: raise CallError
if self.atframeheader: raise CallError if self.atframeheader: raise CallError
# Note that this won't raise EOFError for a partial frame # Note that this won't raise EOFError for a partial frame
# since there is no easy way to tell whether a seek # since there is no easy way to tell whether a seek
@ -636,6 +756,70 @@ def skipnextframedata(self, ds, cs):
self.framecount = self.framecount + 1 self.framecount = self.framecount + 1
# Derived class implementing random access
class RandomVinFile(BasicVinFile):
def initfp(self, fp, filename):
self = BasicVinFile.initfp(self, fp, filename)
self.index = []
return self
def warmcache(self):
if len(self.index) == 0:
self.rewind()
while 1:
try: dummy = self.skipnextframe()
except EOFError: break
else:
print '[RandomVinFile.warmcache(): too late]'
self.rewind()
def getnextframeheader(self):
if self.framecount < len(self.index):
return self._getindexframeheader(self.framecount)
if self.framecount > len(self.index):
raise AssertError, \
'managed to bypass index?!?'
rv = BasicVinFile.getnextframeheader(self)
if self.canseek:
pos = self.fp.tell()
self.index.append(rv, pos)
return rv
def getrandomframe(self, i):
t, ds, cs = self.getrandomframeheader(i)
data, cdata = self.getnextframedata()
return t, ds, cs
def getrandomframeheader(self, i):
if i < 0: raise ValueError, 'negative frame index'
if not self.canseek:
raise Error, self.filename + ': can\'t seek'
if i < len(self.index):
return self._getindexframeheader(i)
if len(self.index) > 0:
rv = self.getrandomframeheader(len(self.index)-1)
else:
self.rewind()
rv = self.getnextframeheader()
while i > self.framecount:
self.skipnextframedata()
rv = self.getnextframeheader()
return rv
def _getindexframeheader(self, i):
(rv, pos) = self.index[i]
self.fp.seek(pos)
self.framecount = i
self.atframeheader = 0
self.eofseen = 0
self.errorseen = 0
return rv
# Basic class for writing CMIF video files
class BasicVoutFile(VideoParams): class BasicVoutFile(VideoParams):
def init(self, filename): def init(self, filename):
@ -649,7 +833,7 @@ def initfp(self, fp, filename):
self = VideoParams.init(self) self = VideoParams.init(self)
self.fp = fp self.fp = fp
self.filename = filename self.filename = filename
self.version = 3.0 # In case anyone inquires self.version = 3.0 # In case anyone inquries
self.headerwritten = 0 self.headerwritten = 0
return self return self
@ -692,7 +876,10 @@ def writeframe(self, t, data, cdata):
def writeframeheader(self, t, ds, cs): def writeframeheader(self, t, ds, cs):
if not self.headerwritten: self.writeheader() if not self.headerwritten: self.writeheader()
if not self.atheader: raise CallError if not self.atheader: raise CallError
self.fp.write(`(t, ds, cs)` + '\n') data = `(t, ds, cs)`
n = len(data)
if n < 63: data = data + ' '*(63-n)
self.fp.write(data + '\n')
self.atheader = 0 self.atheader = 0
def writeframedata(self, data, cdata): def writeframedata(self, data, cdata):
@ -705,11 +892,11 @@ def writeframedata(self, data, cdata):
# Classes that combine files with displayers and/or grabbers: # Classes that combine files with displayers and/or grabbers:
class VinFile(BasicVinFile, Displayer): class VinFile(RandomVinFile, Displayer):
def initfp(self, fp, filename): def initfp(self, fp, filename):
self = Displayer.init(self) self = Displayer.init(self)
return BasicVinFile.initfp(self, fp, filename) return RandomVinFile.initfp(self, fp, filename)
def shownextframe(self): def shownextframe(self):
t, data, cdata = self.getnextframe() t, data, cdata = self.getnextframe()
@ -739,8 +926,9 @@ def test():
vin.initcolormap() vin.initcolormap()
t0 = time.millitimer() t0 = time.millitimer()
while 1: while 1:
try: t = vin.shownextframe() try: t, data, cdata = vin.getnextframe()
except EOFError: break except EOFError: break
dt = t0 + t - time.millitimer() dt = t0 + t - time.millitimer()
if dt > 0: time.millisleep(dt) if dt > 0: time.millisleep(dt)
vin.showframe(data, cdata)
time.sleep(2) time.sleep(2)