From f930c453550ca201e441f266623c3eb40fd6d9af Mon Sep 17 00:00:00 2001 From: Jacob Lifshay Date: Mon, 26 Sep 2022 21:05:38 -0700 Subject: [PATCH] add WIP jpeg decoder demo this includes a tiny test jpeg that's <2kB, so should be fine to be in git. --- .../test/algorithms/jpeg/__init__.py | 0 .../test/algorithms/jpeg/rainbow_smiley.jpg | Bin 0 -> 1781 bytes .../test/algorithms/jpeg/svp64_jpeg_decode.py | 131 ++++++++++++++++++ 3 files changed, 131 insertions(+) create mode 100644 src/openpower/test/algorithms/jpeg/__init__.py create mode 100644 src/openpower/test/algorithms/jpeg/rainbow_smiley.jpg create mode 100644 src/openpower/test/algorithms/jpeg/svp64_jpeg_decode.py diff --git a/src/openpower/test/algorithms/jpeg/__init__.py b/src/openpower/test/algorithms/jpeg/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/openpower/test/algorithms/jpeg/rainbow_smiley.jpg b/src/openpower/test/algorithms/jpeg/rainbow_smiley.jpg new file mode 100644 index 0000000000000000000000000000000000000000..739823bc7e7595b16ecbc46565e705a043837b62 GIT binary patch literal 1781 zcmb7Edpy&7AOCH?&1Q^^t%Tz)Qg|v(7Y++4bDuW1oYK*GgymWs!{gd<9~C=EE*ZJ! z6k?H@C-*pQny?qrbeI*P$#u^n>;VV_ z0-TjTKr#u~0w5InjZ(m*gd^c_7z~a=AY_p8D0z7~l$;z|K?#Faz$(beVfJ9K$~Zh8 zFR!>)We-k835UmhGl78885mp^4wuED<mrG!eu|0P49APj)ZU=d2%hmp!Ur*Pr!{`e!Zx)IXf`yc@L zzdisUgFwP%QBuhS3qYW75QM@Ie{X?6VE`yK(mw32bjm+GA3;hgSH|ldx%qloG6Beg z5NSdX3s?hlWJ-)Om*OXs*-XD~nAYatrZ^IHrL}iS#Jd0w*L!wx_>zz6WEeYH(18fh z{lt<0FL}Y5itnhtA^GkaW)v~;O#jQ^b^{ToYQ2LTI)MHy@d0YmLN6k2S*wI$SG4IJ zR-Wf zEvqju@ka3>hIx~P!dmc^-Jq76>~Pn!!<#g!vJd}_V@qbhY?^`J8n@>UySb~%eQkvT zZ&nZKezgR6V+;m*?TqfSrX#u=I_gq(;h%JV46;o)XHmA;*BBJZzz)9?O*mH(NVA3o zJu$h1XWn?M8P$)iu++1iGcQ+S)$0NF;u~B0}j>{^-1`h@;fZraJR`jYg~g z+{`UCZw85RRpr(#TADvffS9gXOY5AogT5WchU>Acj%a4il$!B`@8RcSQV!ZJMdO2> zO*qQ*I%0#VslM8IS1b8xNP9!8@YeD{%2N)zlUP&ZQ2eYibzvB$yOf1ytGp_jaz756 z*R?*BKe}1;-TEZ?WMqbDOs=d%5XFe@!k>yg!E(K}TvoM7cRyN4PxSdBKnVHPW zq5jL-w%J$I&TkEMc0rP!Kf-n{4VoR`8H%t~q64KIF2un>lqP zkaIh^P|eNVooQV?<>F%9GySgOY0XhWAV5o{?`N3ZZaZxhe|ny6PmJ9C0J&OcUo5PD zonz$%n>nZ+J;Fi7(TciK1oA^61^;k$Y*^a4Z)Yp~FT~#b+|~4RgWR0flHxbLj7Q-`*X&w<~)v{8{Qs`fHQ~ApPrFgU$v@m|XqHH{ReQ zB5Io&I_asUtZ%54;F0nLLk%;35K-pRCIKouEW{UN)2h#ScFgQrQiN1Tlc^nNn~N%<$^Z#~t;v{b?(5s{8;Q5D zcf8t7h0l@(16|fErS}1^F!I>jyYq8Dy?V=bmKz`@1{*ELGoroLi}gV^M0C(9f2?xa igw@0;$-U9{HiRt5?M{N4O_;TM;O1h7e)vk4jQ None + if table is None: + table = {} + self.table = table + self.table_id = table_id + self.is_ac = is_ac + + @staticmethod + def decode(data): + # type: (bytes) -> HuffmanTable + id_offset = 0 + counts_offset = 1 + retval = HuffmanTable(table_id=data[id_offset] & 0xF, + is_ac=data[id_offset] & 0xF0 != 0) + num_counts = 16 + offset = counts_offset + num_counts + code = 0 + for i in range(num_counts): + bit_length = 1 + i + count = data[counts_offset + i] + code <<= 1 + for _ in range(count): + value = data[offset] + offset += 1 + code_str = bin(code)[:2].rjust(bit_length, "0") + retval.table[code_str] = value + code += 1 + + return retval + + +@plain_data() +class StartOfScanComponent: + __slots__ = "component_id", "dc_huffman_table_id", "ac_huffman_table_id" + + def __init__(self, component_id, dc_huffman_table_id, ac_huffman_table_id): + # type: (int, int, int) -> None + self.component_id = component_id + self.dc_huffman_table_id = dc_huffman_table_id + self.ac_huffman_table_id = ac_huffman_table_id + + +def parse_start_of_scan(data): + # type: (bytes) -> list[StartOfScanComponent] + offset = 0 + color_component_count = data[offset] + offset += 1 + retval = [] + for _ in range(color_component_count): + retval.append(StartOfScanComponent( + component_id=data[offset], + dc_huffman_table_id=data[offset + 1] >> 4, + ac_huffman_table_id=data[offset + 1] & 0xF, + )) + offset += 2 + # ignore the rest + return retval + + +def extract_demo_bitstream(data): + # type: (bytes) -> tuple[bytes, HuffmanTable] + assert data.startswith(b"\xFF\xD8\xFF"), "not a jpeg" + dc_huffman_tables = {} # type: dict[int, HuffmanTable] + ac_huffman_tables = {} # type: dict[int, HuffmanTable] + start_of_scan_data = [] # type: list[StartOfScanComponent] + + offset = 0 + while True: + if data[offset] != 0xFF: + offset += 1 + continue + offset += 1 + if data[offset] == 0: + offset += 1 + continue + while data[offset] == 0xFF: + offset += 1 + marker = data[offset] + offset += 1 + if 0xD0 <= marker <= 0xD8: # restart marker + continue + if marker == 0xD8: # start of image + break + if marker == 0xD9: # end of image + assert False, "missing JPEG image data" + segment_size = data[offset] << 8 + segment_size |= data[offset + 1] + assert segment_size >= 2, "invalid marker segment size" + segment_data = data[offset + 2:offset + segment_size] + assert len(data) >= offset + segment_size, \ + "file truncated before end of marker segment" + offset += segment_size + if 0xE0 <= marker <= 0xEF: # APP0 through APP15 + continue # ignored + if marker == 0xDB: # DQT -- define quantization table + continue # ignored + if marker == 0xC0: # SOF0 -- start of frame + continue # ignored + if marker == 0xC4: # DHT -- define huffman table + table = HuffmanTable.decode(segment_data) + if table.is_ac: + ac_huffman_tables[table.table_id] = table + else: + dc_huffman_tables[table.table_id] = table + continue + if marker == 0xDA: # SOS -- start of scan + start_of_scan_data = parse_start_of_scan(segment_data) + print(start_of_scan_data) + continue + assert False, f"0xFF{marker:02X}: {segment_data}" + # plan is to just extract a minimal huffman-compressed bitstream that can + # be used for the assembly algorithm demo. this will just be the first + # chunk of the file, not the whole thing. + raise NotImplementedError # TODO: finish + + +# DEMO_BITSTREAM, DEMO_HUFFMAN_TABLE = extract_demo_bitstream(RAINBOW_SMILEY) -- 2.30.2