def dump(x): c = palette[x] out.write( strtoio( pack([ (getbit(c, 5) * 2 + getbit(c, 2)) * 85, (getbit(c, 4) * 2 + getbit(c, 1)) * 85, (getbit(c, 3) * 2 + getbit(c, 0)) * 85, ])))
def test_getbit(self): self.assertEqual(getbit(33, 0), 1) self.assertEqual(getbit(32, 0), 0) self.assertEqual(getbit(128, 7), 1) self.assertEqual(getbit(128, 0), 0)
def convert( input_image_stream, output_image_stream, arte, newsroom, cols, rows, skip, ignore_header_errors, ): def clip(v): return 255 if v > 255 else (0 if v < 0 else v) # imitate +I and -I colours using Coco palette br2 = [ pack(x) for x in [[0, 0, 0], [255, 85, 0], [0, 170, 255], [255, 255, 255]] ] # take names "blue" and "red" too literally like many "patched for Coco3" # programs br3 = [ pack(x) for x in [[0, 0, 0], [255, 0, 0], [0, 0, 255], [255, 255, 255]] ] # probably not exact... semig = [ pack(x) for x in [ [0, 0, 0], [0, 255, 0], [255, 255, 0], [0, 0, 255], [255, 0, 0], [255, 255, 255], [0, 211, 170], [204, 0, 255], [255, 128, 0], ] ] f = input_image_stream out = output_image_stream if skip: f.read(skip) if newsroom: head = iotostr(f.read(2)) cols = ord(head[0]) * 8 rows = ord(head[1]) else: head = iotostr(f.read(5)) if ord(head[0]) != 0: sys.stderr.write("bad first byte in header\n") if not ignore_header_errors: return False if not rows: size = ord(head[1]) * 256 + ord(head[2]) rows = 8 * size // cols if cols * rows // 8 != size: sys.stderr.write( "data length {} in header would be closest to {}x{} but " "that would be {} bytes\n".format(size, cols, rows, cols * rows // 8)) if not ignore_header_errors: return False out.write(strtoio("P6\n{} {}\n255\n".format(cols, rows))) for jj in range(rows): row = iotostr(f.read(cols >> 3)) oy = r2 = g2 = b2 = 0 for vv in row: v = ord(vv) if arte == PIXEL_MODE_BW: for k in range(8): out.write(strtoio(br2[getbit(v, 7 - k) * 3])) elif (arte == PIXEL_MODE_BR) or (arte == PIXEL_MODE_RB): x = -100 if arte == PIXEL_MODE_BR else 100 # this is using the exact YIQ-to-RGB formula, but the rest is # just trial-and-error of what looks ok, without actually using # the spec of NTSC and/or VDG/GIME. more pixel options could # be added to allow variants on : brightness/contrast ; # saturation ; double-resolution for greater detail in # emulation ; different smoothing and horiz phase ; and # replacing ±I colours by ±V colours (green-purple of PAL and # of SECAM). for k in range(8): ny = getbit(v, 7 - k) * 255 y = (oy + ny + (ny >> 2)) >> 1 i = (x * (y - oy)) >> 7 r = clip(int((y + 0.9563 * i))) g = clip(int((y - 0.2721 * i))) b = clip(int((y - 1.1070 * i))) out.write( strtoio( pack([(r + r2) >> 1, (g + g2) >> 1, (b + b2) >> 1]))) oy = ny x = -x r2 = r g2 = g b2 = b elif arte == PIXEL_MODE_BR2: for k in range(4): out.write( strtoio(br2[getbit(v, 7 - k - k) * 2 + getbit(v, 6 - k - k)] * 2)) elif arte == PIXEL_MODE_RB2: for k in range(4): out.write( strtoio(br2[getbit(v, 7 - k - k) + getbit(v, 6 - k - k) * 2] * 2)) elif arte == PIXEL_MODE_BR3: for k in range(4): out.write( strtoio(br3[getbit(v, 7 - k - k) * 2 + getbit(v, 6 - k - k)] * 2)) elif arte == PIXEL_MODE_RB3: for k in range(4): out.write( strtoio(br3[getbit(v, 7 - k - k) + getbit(v, 6 - k - k) * 2] * 2)) elif arte == PIXEL_MODE_S10: for k in range(4): out.write( strtoio(semig[1 + getbit(v, 7 - k - k) + getbit(v, 6 - k - k) * 2] * 2)) elif arte == PIXEL_MODE_S11: for k in range(4): out.write( strtoio(semig[5 + getbit(v, 7 - k - k) + getbit(v, 6 - k - k) * 2] * 2)) return True
def convert(input_image_stream, output_image_stream): def debug(x): sys.stderr.write("{}\n".format(x)) def dump(x): c = palette[x] out.write( strtoio( pack([ (getbit(c, 5) * 2 + getbit(c, 2)) * 85, (getbit(c, 4) * 2 + getbit(c, 1)) * 85, (getbit(c, 3) * 2 + getbit(c, 0)) * 85, ]))) # Read basic structure # - is is a 192 or 384 row image? # - motifs - patterns used for filling shapes stored in the image file f = input_image_stream cols = 320 pictyp = ord(iotostr(f.read(1))) rows = (getbit(pictyp, 7) + 1) * 192 sans_motifs = getbit(pictyp, 0) != 0 debug("{}x{}, 16 couleurs, sans_motifs={}".format(cols, rows, sans_motifs)) # Get palette information palette = [ord(iotostr(f.read(1))) for ii in range(16)] anirat = ord(iotostr(f.read(1))) cycrat = ord(iotostr(f.read(1))) cm3cyc = [ord(iotostr(f.read(1))) for ii in range(8)] aniflg = ord(iotostr(f.read(1))) & 0x80 != 0 cycflg = ord(iotostr(f.read(1))) & 0x80 != 0 debug("palette={}".format(palette)) debug("cm3cyc={} cycrat={} cycflg={} anirat={} aniflg={}".format( cm3cyc, cycrat, cycflg, anirat, aniflg)) if not sans_motifs: iotostr(f.read(243)) linbuf = [0] * 160 buff1 = [0] * 20 buff2 = [] # Start outputting image out = output_image_stream out.write(strtoio("P6\n{} {}\n255\n".format(cols, rows))) for ii in range(getbit(pictyp, 7) + 1): lines = ord(iotostr(f.read(1))) for jj in range(lines): u = 0 y = 0 bitu = 7 bity = 7 x = 0 a = None contr = ord(iotostr(f.read(1))) if contr < 128: for kk in range(20): buff1[kk] = ord(iotostr(f.read(1))) buff2 = [] for kk in range(contr): buff2.append(ord(iotostr(f.read(1)))) for kk in range(160): if contr >= 128: a = ord(iotostr(f.read(1))) else: cc = getbit(buff1[u], bitu) bitu = bitu - 1 if bitu < 0: bitu = 7 u = u + 1 if cc == 0: a = linbuf[(x - 1) % 160] else: cc = getbit(buff2[y], bity) bity = bity - 1 if bity < 0: bity = 7 y = y + 1 if cc == 0: a = linbuf[x] else: a = ord(iotostr(f.read(1))) linbuf[x] = a dump(a >> 4) dump(a & 15) x = x + 1 # Look for extra junk at the end of the file extra = 0 while iotostr(f.read(1)) != "": extra = extra + 1 pass if extra > 0: debug("{} octets de trop".format(extra))