From: daz Date: 2004-03-16T15:34:35+09:00 Subject: Re: binary data Alwin Blok wrote: > > I was playing wih the framebuffer device, using pack, I was able to get > the bits where I wanted, but It's not handy when you want gradients and > other fancy stuff > > When I use > > File.open('/dev/fb0','w') do |fb| > (800*600).times do > fb.putc 0b0111110000000000 > end > end > > I get half my screen filled, Which is obvious since putc is short for > putchar (i assume), hence 8bits > (the screen is 16 bits color) For that specific case, 2 x putc File.open('/dev/fb0','w') do |fb| (800*600).times do fb.putc 0b011111000 fb.putc 0 end end .... but, I know, you want something more flexible. It intrigues me that you'd want to write, say, 10 bits when you haven't specified left, right or no padding. Here are all the screen size options on my display adapter. # Test width / height exactly divisible by 8 ss = lambda {|w, h| p [w % 8 == 0, w/8, h % 8 == 0, h/8]} ss[ 640, 480] ss[ 800, 600] ss[1024, 768] ss[1152, 864] ss[1280, 1024] ss[1600, 1200] #-> [true, 80, true, 60] #-> [true, 100, true, 75] #-> [true, 128, true, 96] #-> [true, 144, true, 108] #-> [true, 160, true, 128] #-> [true, 200, true, 150] Anyway, see if any of this helps ... # Constraint: #------------ # If the bit pattern is to be given as an integer, # the pattern length also needs to given because # leading zeros have no significance. puts puts 0b0110.to_s(2) puts 0b00110.to_s(2) puts 0b00000000000000110.to_s(2) puts # Strings have a slight advantage in this respect. puts '0110'.length puts '00110'.length puts '00000000000000110'.length # OTOH, with known length, leading zeros # need not be given. fmt = "%010b\n" # (e.g. these examples are equivalent): puts format(fmt, 0b110) puts format(fmt, 0b0110) puts format(fmt, 0b00000000000000110) puts format(fmt, 6) #-> 110 #-> 110 #-> 110 #-> #-> 4 #-> 5 #-> 17 #-> 0000000110 #-> 0000000110 #-> 0000000110 #-> 0000000110 Here's a buffered write to play with: (messed up by comments & display stuff) #----------------------------------------------------------------- $watch = true n = 0b1001_11101101_00010000 nbits = 20 # n represents 20 bits ## Normally start with: ##> buff = bufn = 0 buff = 0b10 # example remainder from "previous use" bufn = 3 # buff represents 3 bits == 010 ## Prepend bits (less than a full byte) to new bits n |= (buff << nbits) ## Temporarily adjust the number of bits to write this time round nb = nbits + bufn if $watch printf("START (%2d) x[%08x] b[%0#{nb}b]\n\n", nb, n, n) end while nb >= 8 nb -=8 mask = 0xff << nb ## find the first full byte n8 = (n & mask) ## temporarily ignore the rest if $watch printf("mask x[%08x] b[%0#{nb+8}b]\n", mask, mask) printf("tmp x[%08x] b[%0#{nb+8}b]\n", n8, n8) end n ^= n8 ## remove these 8 bits from the bits to be written n8 >>= nb ## move these 8 bits down to bits 7......0 if $watch printf("putc x[%08x] b[%08b]\n\n", n8, n8) printf("NEXTn (%2d) x[%08x] b[%0#{nb}b]\n", nb, n, n) puts '-'*10 end end buff = n ## save partial last byte for next time round bufn = nb ## AND the exact number of bits with significance if $watch printf("-> buff %0#{bufn}b\n", buff) end #----------------------------------------------------------------- ### And here's how it might look in the real world: class Screen def initialize(w, h, nbits = 16) @width = w @height = h @nbits = nbits @screen = File.open('tbinary.dat', 'wb') @screen.sync = true @buff = 0 @bufn = 0 # Number of unwritten bits in buffer end def close ## @buff <<= 8-@bufn # align left in byte @screen.putc(@buff) # write to screen @screen.close end def write(patt, nbits = @nbits) patt |= (@buff << nbits) nb = nbits + @bufn while nb >= 8 nb -=8 mask = 0xff << nb ## find the first full byte n8 = (patt & mask) ## temporarily ignore the rest patt ^= n8 ## remove these 8 bits from the bits to be written n8 >>= nb ## move these 8 bits down to bits 7......0 @screen.putc(n8) # write to screen end @buff = patt ## save partial last byte for next time round @bufn = nb ## AND the exact number of bits with significance end end fb = Screen.new(800, 600, 10) 5.times {fb.write(0b11111010, 16)} fb.write 0b1101101101 # length 10 from Screen.new fb.write 0b1010111010 fb.write(0b0011001100, 40) fb.close ## See what happened ... File.open('tbinary.dat', 'rb') do |fb| bd = fb.read puts bd.unpack('H*') end #-> 00fa00fa00fa00fa00fadb6ba00000000cc0 #----------------------------------------------------------------- > > So my real question is: > What command do I use to put an arbitrary amount of bits? putc > I can think of some ways, but I hate it when it feels like a workaround. I know what you mean. It's not a workaround, it's a tricky problem. Once you have a method which does what you want, you can forget about the complexity and advance to the next stage like adding Screen#fill(pattern). > Thanks > -Alwin OK, daz