summaryrefslogtreecommitdiff
path: root/jpg
diff options
context:
space:
mode:
Diffstat (limited to 'jpg')
-rw-r--r--jpg/Makefile16
-rwxr-xr-xjpg/conv165
-rwxr-xr-xjpg/conv21319
-rwxr-xr-xjpg/conv341
-rw-r--r--jpg/getword7
-rw-r--r--jpg/jpg.c948
-rw-r--r--jpg/jpg.h2
-rw-r--r--jpg/test1.jpgbin0 -> 26277 bytes
-rw-r--r--jpg/test2.jpgbin0 -> 120169 bytes
-rw-r--r--jpg/test3.jpgbin0 -> 107826 bytes
-rw-r--r--jpg/test4.jpgbin0 -> 61909 bytes
-rw-r--r--jpg/z2.c160
12 files changed, 2558 insertions, 0 deletions
diff --git a/jpg/Makefile b/jpg/Makefile
new file mode 100644
index 0000000..c7ea63e
--- /dev/null
+++ b/jpg/Makefile
@@ -0,0 +1,16 @@
+all: z2 jpeg.S
+
+z2: z2.c jpg.o
+ gcc -O2 -Wall z2.c jpg.o -o z2
+
+jpg.o: jpg.c
+ gcc -O2 -Wall -c jpg.c
+
+jpeg.S jpeg.inc: jpg.c
+ gcc -fomit-frame-pointer -S -Os -Wall -c jpg.c
+ ./conv1 jpg.s >jpeg1.s
+ ./conv2 -o jpeg2.s jpeg1.s
+ ./conv3 jpeg2.s >jpeg.S 2>jpeg.inc
+
+clean:
+ rm -f *~ *.o *.i *.s *.S *.inc *.pnm z2
diff --git a/jpg/conv1 b/jpg/conv1
new file mode 100755
index 0000000..933cb85
--- /dev/null
+++ b/jpg/conv1
@@ -0,0 +1,65 @@
+#! /usr/bin/perl
+
+# gcc -save-temps -fomit-frame-pointer -Os -Wall -c jpg.c
+# convert: .data -> .text
+# ## add: .code16gcc
+# convert: .local/.comm foo,bar,??? -> foo: .space bar,0
+# remove: .size, .type, .align
+# remove .section .note...
+# remove .ident
+# replace getword
+
+sub fix_getword;
+
+@f = (<>);
+
+$getword = `cat getword`;
+
+for (@f) {
+ undef $_ if /^\s*\.(size|align|p2align|text|section|ident|local|data|file)/;
+ $_ = "\n" if /^\s*\.type/;
+ if(/^\s*\.globl/) {
+ push @globl, "\t$_";
+ undef $_;
+ }
+
+ if(/^\s*\.comm\s+(\S+),(\d+),\d+\s*$/) {
+ push @comm, ( "\n", "$1:\n", "\t.space\t$2,0\n" );
+ undef $_;
+ }
+}
+
+unshift @globl, ( "\t.file\t\"jpeg.S\"\n", "\t.text\n",
+ # "\t\.code16gcc\n",
+ "\n"
+);
+
+fix_getword;
+
+$_ = join '', @globl, @comm, @f; s/\n{3,}/\n\n/g; @f = split /\n/;
+
+print "$_\n" for @f;
+
+for (qw ( getword )) {
+ if($fixes{$_} != 1) {
+ print STDERR "error: fix failed: $_\n"
+ }
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub fix_getword
+{
+ for (@f) {
+ if(/^getword:/ .. /^\s*ret\b/) {
+ if(/^getword:/) {
+ $_ = $getword;
+ $fixes{getword}++;
+ }
+ else {
+ undef $_;
+ }
+ }
+ }
+}
+
diff --git a/jpg/conv2 b/jpg/conv2
new file mode 100755
index 0000000..9d42fc3
--- /dev/null
+++ b/jpg/conv2
@@ -0,0 +1,1319 @@
+#! /usr/bin/perl
+
+# tack pointer usage and assign proper segment registers
+
+use Getopt::Long;
+Getopt::Long::Configure("bundling");
+
+sub add_prefix;
+sub get_arg;
+sub decode_instr;
+sub decode_arg;
+sub trace_it;
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub is_ref;
+sub is_reg;
+sub is_num;
+sub is_sym;
+sub const_add;
+sub const_eval;
+sub const_eval_addr;
+sub push_on_stack;
+sub pop_from_stack;
+sub start_trace;
+sub trace_it;
+sub clone_state;
+sub show_state;
+sub start_trace;
+sub set_state;
+sub del_state_context;
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+$re_reg = '%[a-z]+';
+$re_symbol = '[._a-zA-Z][\._a-zA-Z0-9]*';
+$re_num = '[\-\+]?\d+';
+
+%full_reg = (
+ '%al' => '%eax',
+ '%ah' => '%eax',
+ '%ax' => '%eax',
+ '%bl' => '%ebx',
+ '%bh' => '%ebx',
+ '%bx' => '%ebx',
+ '%cl' => '%ecx',
+ '%ch' => '%ecx',
+ '%cx' => '%ecx',
+ '%dl' => '%edx',
+ '%dh' => '%edx',
+ '%dx' => '%edx',
+ '%si' => '%esi',
+ '%di' => '%edi',
+ '%bp' => '%ebp',
+ '%sp' => '%esp',
+);
+
+
+$opt_verbose = 0;
+$opt_out_file = '-';
+
+GetOptions(
+ 'v' => sub { $opt_verbose++ },
+ 'q' => sub { $opt_verbose-- },
+ 'o=s' => \$opt_out_file,
+);
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+$static_data_seg = 'fs';
+# see trace_it() for more configs
+
+while(<>) {
+ chomp;
+
+ $text{$.}{orig} = $_;
+
+ if(s/^\s*($re_symbol)://) {
+ $symbol{$1}{line} = $.;
+ $c = $1;
+ $context = $c unless $1 =~ /^\.L/;
+ $ofs = 0;
+ }
+
+ s/\s*#.*//;
+ s/^\s*//;
+
+ if(/^.globl\s+(\S+)$/) {
+ $global{$1} = $.;
+ undef $_;
+ }
+
+ if(defined($ofs) && /^\.(long|byte)\s+(\d+)$/) {
+ $ofs += 1 if $1 eq 'byte';
+ $ofs += 4 if $1 eq 'long';
+ if($context) {
+ $symbol{$context}{size} = $ofs;
+ $seg = $symbol{$context}{seg};
+ if($seg && $seg ne 'cs') {
+ warn "$.: moving $context from \"$seg\" to \"cs\"\n"
+ }
+ $symbol{$context}{seg} = "cs";
+ }
+ undef $_;
+ }
+ else {
+ undef $ofs unless /^\s*$/;
+ }
+
+ if(/^\.space\s+(\d+)\s*\,\s*(\d+)$/) {
+ if($2) {
+ warn "$.: $context has nonzero data\n";
+ }
+ if($context) {
+ $symbol{$context}{size} = $1;
+ $seg = $symbol{$context}{seg};
+ if($seg && $seg ne $static_data_seg) {
+ warn "$.: moving $context from \"$seg\" to \"$static_data_seg\"\n"
+ }
+ $symbol{$context}{seg} = $static_data_seg;
+ }
+ undef $_;
+ }
+
+ $text{$.}{op} = $_;
+ $text{$.}{context} = $context;
+}
+
+for (sort { $a <=> $b } keys %text) {
+ decode_instr $_;
+}
+
+trace_it;
+
+
+for (keys %text) {
+ add_prefix $_;
+}
+
+open F, ">$opt_out_file";
+
+for (sort { $a <=> $b } keys %text) {
+ if($text{$_}{new}) {
+ print F "\t# $text{$_}{new_comment}\n" if $opt_verbose >= 0 && $text{$_}{new_comment};
+ printf F "%s\n", $text{$_}{new};
+ }
+ else {
+ printf F "%s\n", $text{$_}{orig};
+ }
+ if($opt_verbose >= 2 && $text{$_}{instr}) {
+
+ if($opt_verbose >= 3) {
+ $state = $text{$_}{state};
+ print F "\t # $state->{'..ilog'}\n" if $state->{'..ilog'};
+ if($state->{'..addr'}) {
+ print F "\t # mem ref: " . join(', ', @{$state->{'..addr'}}) . "\n";
+ }
+ print F show_state("\t #");
+ }
+ }
+}
+
+if($opt_verbose >= 2) {
+ print F "\n# symbols:\n";
+ for (sort { $symbol{$a}{line} <=> $symbol{$b}{line} } keys %symbol) {
+ printf F "# %s (%sseg = %s, size %d)\n",
+ $_,
+ $global{$_} ? "global, " : "",
+ $symbol{$_}{seg},
+ $symbol{$_}{size};
+ }
+}
+
+close F;
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub add_prefix
+{
+ local $_;
+ my ($l, $state, $seg, $s, @i, $n, @a, $i, @c, $d);
+
+ $l = shift;
+
+ return if !($state = $text{$l}{state});
+
+ return unless $text{$l}{instr};
+
+ for (@{$state->{'..addr'}}) {
+ undef $s;
+ @i = split /\+/;
+ next if !$i[0];
+ $s = $symbol{$i[0]}{seg} if is_sym($i[0]) && $symbol{$i[0]}{seg};
+
+ if(!$s || ($seg && $s ne $seg)) {
+ warn "$l: can't determine segment\n"
+ }
+ $seg = $s if $s;
+ }
+
+ for ($i = 0; $i < @{$text{$l}{arg}}; $i++) {
+ if($seg && $state->{'..addr'}[$i]) {
+ $d = $text{$l}{arg}[$i]{seg};
+ $d = 'ds' unless $d;
+ $a[$i] = "%$seg:" if $seg ne $d;
+ push @c, "$text{$l}{arg}[$i]{orig} = %$seg:$state->{'..addr'}[$i]";
+ }
+ $a[$i] .= $text{$l}{arg}[$i]{orig};
+ }
+
+ $n = "\t$text{$l}{instr}";
+ $n .= "\t" . join(', ', @a) if @a;
+
+ $text{$l}{new_comment} = join(', ', @c) if @c;
+ $text{$l}{new} = $n;
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub get_arg
+{
+ local $_;
+
+ $_ = shift;
+
+ if(/^([^,()]*(\(.*?\))?[^,()]*)/) {
+ return $1;
+ }
+
+ return undef;
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub decode_instr
+{
+ local $_;
+ my ($l, $op, $instr, $arg1, $arg2, $arg3, @arg, $arg, $is_branch);
+
+ $l = shift;
+
+ $op = $text{$l}{op};
+
+ return unless $op =~ s/^(\S+)\s*//;
+
+ $instr = $1;
+
+ return if $instr =~ /^\.(file|text|code16gcc)$/;
+
+ $is_branch = 1 if $instr =~ /^(call|j)/;
+
+ $arg1 = get_arg $op;
+ substr($op, 0, length($arg1)) = "";
+ $op =~ s/^,\s*//;
+
+ $arg2 = get_arg $op;
+ substr($op, 0, length($arg2)) = "";
+ $op =~ s/^,\s*//;
+
+ $arg3 = get_arg $op;
+ substr($op, 0, length($arg3)) = "";
+ $op =~ s/^,\s*//;
+
+ if($op) {
+ warn "$l: error splitting args\n"
+ }
+
+# print "[$instr] [$arg1] [$arg2] [$arg3]\n";
+
+ $text{$l}{instr} = $instr;
+
+ $. = $l;
+
+ if($arg1) {
+ $arg = decode_arg $arg1, $is_branch;
+ push @arg, $arg;
+ }
+
+ if($arg2) {
+ $arg = decode_arg $arg2, $is_branch;
+ push @arg, $arg;
+ }
+
+ if($arg3) {
+ $arg = decode_arg $arg3, $is_branch;
+ push @arg, $arg;
+ }
+
+ $text{$l}{arg} = [ @arg ];
+
+# for $arg (@arg) {
+# print "$arg->{orig},\tval = $arg->{val}\n";
+# }
+
+# print "$text{$l}{arg}[0]{orig}\n";
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub decode_arg
+{
+ local $_;
+ my ($is_branch, $arg, $p, $v, $seg, $l, @i);
+
+ $_ = shift;
+ $is_branch = shift;
+
+ $arg->{orig} = $_;
+
+ if(/^(\*?)($re_reg)$/o) {
+ $arg->{val} = $2;
+
+ if($1) {
+ warn "$.: arg with \"*\"?\n" unless $is_branch;
+ }
+
+ return $arg;
+ }
+
+ if(s/^([*\$]?)([^*,:%()]+)//) {
+ $p = $1;
+ $v = $2;
+ if($v =~ s/^($re_symbol)([\+\-])?//i) {
+ $v = "-$v" if $2 eq '-';
+ $l = $1;
+ }
+
+ if($v && $v !~ /^$re_num$/o) {
+ warn "$.: $v is not a number\n";
+ }
+
+ if($l) {
+ warn "$.: unknown symbol $l\n" unless $symbol{$l};
+ $v = "+$v" if $v;
+ $arg->{val} = "$l$v";
+ }
+ else {
+ $arg->{val} = $v;
+ }
+
+ if($is_branch) {
+ warn "$.: arg with \"$p\"?\n" if $p eq '$';
+ $arg->{val} = "*$arg->{val}" if $p eq '*';
+ }
+ else {
+ warn "$.: arg with \"$p\"?\n" if $p eq '*';
+ $arg->{val} = "*$arg->{val}" unless $p eq '$';
+ }
+ }
+
+ if(/^\(.+\)$/) {
+ s/(^\(|\)$)//g;
+ @i = split /\s*,\s*/;
+ $i[2] = 1 unless $i[2];
+ if($i[0] eq '%esp' || $i[0] eq '%ebp') {
+ $arg->{seg} = 'ss';
+ }
+ if($i[1] && $i[1] eq $i[0]) {
+ $i[0] = undef;
+ $i[2]++;
+ }
+
+ $arg->{val} = '*' unless $arg->{val};
+
+ $arg->{val} .= "+$i[0]" if $i[0];
+ $arg->{val} .= "+$i[1]" if $i[1];
+ $arg->{val} .= "*$i[2]" if $i[2] > 1;
+
+ $arg->{val} =~ s/^\*\+/*/;
+
+ return $arg;
+ }
+
+ warn "$.: don't understand \"$arg->{orig}\"\n" if $_;
+
+ return $arg;
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+sub is_ref
+{
+ return $_[0] =~ /^\*/o;
+}
+
+
+sub is_reg
+{
+ return $_[0] =~ /^$re_reg$/o;
+}
+
+
+sub is_num
+{
+ return $_[0] =~ /^$re_num$/o;
+}
+
+
+sub is_sym
+{
+ return $symbol{$_[0]};
+}
+
+
+sub const_add
+{
+ local $_;
+ my (@i, $i, $ok, $arg1, $arg2);
+
+ ($arg1, $arg2) = @_;
+
+ if($arg1 eq '?') {
+ ($arg2, $arg1) = ($arg1, $arg2);
+ }
+
+ if($symbol{$arg1}) {
+ ($arg2, $arg1) = ($arg1, $arg2);
+ }
+
+ if(is_num($arg1)) {
+ ($arg2, $arg1) = ($arg1, $arg2);
+ }
+
+ @i = split /\+/, $arg1;
+
+ if(is_num $arg2) {
+ for (@i) {
+ if(/^$re_num$/o) {
+ $_ += $arg2;
+ $ok = 1;
+ last;
+ }
+ elsif($_ eq '?') {
+ $ok = 1;
+ last;
+ }
+ }
+ push @i, $arg2 unless $ok;
+ }
+ elsif(is_sym $arg2) {
+ if(is_num $arg1) {
+ @i = ( $arg2 );
+ push @i, $arg1 if $arg1 != 0;
+ }
+ elsif($arg1 eq '?') {
+ @i = ( $arg2, '?' );
+ }
+ else {
+ @i = ( '?' );
+ }
+ }
+ elsif($arg2 eq '?') {
+ for (@i) {
+ if(/^$re_num$/o || $_ eq '?') {
+ $_ = '?';
+ $ok = 1;
+ last;
+ }
+ }
+ @i = ( '?' ) unless $ok;
+ }
+ else {
+ @i = ( '?' );
+ }
+
+ $i = join '+', @i;
+
+ # print ">>$arg1 + $arg2 = $i<<\n";
+
+ return $i;
+}
+
+
+sub const_eval
+{
+ local $_;
+ my ($arg, @i, @j, $sym, $val, $nosym, $ref, $i, $fact);
+
+ $arg = shift;
+
+ $val = 0;
+
+ $ref = 1 if $arg =~ s/^\*//;
+
+ @i = split /\+/, $arg;
+
+ for (@i) {
+ undef $fact;
+ if(/^%/ && s/\*(\d+)$//) {
+ $fact = $1;
+ undef $fact if $fact == 1;
+ }
+ if(is_reg($_) && defined($state->{$_})) {
+ if(is_num($state->{$_}) && defined($fact)) {
+ $fact *= $state->{$_};
+ push @j, "$fact";
+ }
+ elsif(!defined($fact)) {
+ push @j, split(/\+/, $state->{$_});
+ }
+ else {
+ push @j, '?';
+ }
+ }
+ else {
+ push @j, $_;
+ }
+ }
+
+ for (@j) {
+ if($symbol{$_}) {
+ $nosym = 1 if $sym;
+ $sym = $_;
+ next;
+ }
+
+ if(/^$re_num$/o) {
+ $val += $_ unless $val eq '?';
+ next;
+ }
+
+ $val = '?';
+ }
+
+ $i = '?';
+
+ if(!$nosym) {
+ if($sym && $val) {
+ $i = "$sym+$val";
+ }
+ elsif($sym) {
+ $i = $sym;
+ }
+ elsif(defined($val)) {
+ $i = "$val";
+ }
+ }
+
+ if($ref) {
+ if($state->{$i}) {
+ $i = $state->{$i};
+ }
+ else {
+ $i = '?';
+ }
+ }
+
+ return $i;
+}
+
+
+sub const_eval_addr
+{
+ local $_;
+
+ $_ = shift;
+
+ return $_ if /^${re_reg}$/;
+
+ $_ =~ s/^\*//;
+
+ return const_eval $_;
+}
+
+
+sub push_on_stack
+{
+ my ($new_sp);
+
+ $new_sp = const_add $state->{"%esp"}, -4;
+
+ $state->{"%esp"} = $new_sp;
+ $state->{"$new_sp"} = $_[0];
+}
+
+
+sub pop_from_stack
+{
+ my ($new_sp, $i);
+
+ $new_sp = const_add $state->{"%esp"}, 4;
+
+ $i = $state->{$state->{"%esp"}};
+
+ delete $state->{$state->{"%esp"}};
+ $state->{"%esp"} = $new_sp;
+
+ return $i;
+}
+
+
+sub trace_it
+{
+ undef @branch_states;
+
+ $symbol{stack}{size} = 1000;
+ $symbol{stack}{seg} = 'ss';
+
+ $symbol{jpg}{seg} = 'es';
+ $symbol{pic}{seg} = 'es';
+
+ $state->{"%esp"} = "stack+1000";
+ push_on_stack "jpg";
+ push_on_stack "0+%cs";
+ $state->{"%eip"} = "$symbol{jpeg_get_size}{line}+%cs";
+ push @branch_states, clone_state($state);
+
+ $state->{"%esp"} = "stack+1000";
+ push_on_stack "32";
+ push_on_stack "111";
+ push_on_stack "11";
+ push_on_stack "222";
+ push_on_stack "22";
+ push_on_stack "pic";
+ push_on_stack "jpg";
+ push_on_stack "0+%cs";
+ $state->{"%eip"} = "$symbol{jpeg_decode}{line}+%cs";
+ push @branch_states, clone_state($state);
+
+ undef $state;
+
+ while($state = pop @branch_states) {
+ start_trace;
+ }
+}
+
+
+sub clone_state
+{
+ my ($new_state);
+
+ $new_state->{$_} = $state->{$_} for keys %{$state};
+
+ return $new_state;
+}
+
+
+sub show_state
+{
+ local $_;
+ my ($s);
+
+ for (sort keys %{$state}) {
+ $s .= "$_[0] $_ = $state->{$_}\n" unless /^\.\./;
+ }
+
+ return $s;
+}
+
+
+sub start_trace
+{
+ local $_;
+ my ($op, $c, $ok, $opc, $br);
+
+ $pc = 0;
+
+ $br = @branch_states;
+
+ # print "starting at: $state->{'%eip'} ($br)\n";
+
+ $state->{'..stop'} = undef;
+
+ do {
+ $state->{'..addr'} = undef;
+ $state->{'..ilog'} = undef;
+
+ if($state->{"%eip"} =~ /^(${re_num})\+%cs$/) {
+ $pc = $1;
+ }
+ else {
+ warn "$state->{'%eip'}: oops, got lost\n";
+ return;
+ }
+
+ return if !$pc;
+
+ if(!exists($text{"$pc"})) {
+ warn "$state->{'%eip'}: oops, got lost\n";
+ return;
+ }
+
+ if($state->{"..%oeip"} =~ /^(${re_num})\+%cs$/) {
+ $opc = $1;
+ }
+ else {
+ $opc = 0;
+ }
+
+ $loc = "$opc->$pc";
+
+ $op = $text{"$pc"};
+
+ print "# $loc: $op->{orig}\n" if $opt_verbose >= 2;
+
+ $ok = 0;
+
+ $state->{"..%oeip"} = $state->{"%eip"};
+
+ $state->{"%eip"} = const_add $state->{"%eip"}, 1;
+
+ if($op->{instr}) {
+ if($c = $opcodes{$op->{instr}}) {
+ &$c($op);
+ $ok = 1;
+ }
+ else {
+ warn "$pc: unknown opcode: $op->{instr}\n";
+ }
+ }
+
+ $text{"$pc"}{state} = clone_state;
+
+ print "# $state->{'..ilog'}\n" if $opt_verbose >= 1;
+
+ if($opt_verbose >= 3) {
+ print show_state if $ok;
+ }
+
+ } while(!$state->{'..stop'});
+}
+
+
+sub set_state
+{
+ $state->{$_[0]} = "$_[1]";
+}
+
+
+sub del_state_context
+{
+ local $_;
+
+ return unless $_[0];
+
+ for (keys %text) {
+ undef $text{$_}{state} if $text{$_}{context} eq $_[0];
+ }
+}
+
+
+# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+BEGIN {
+ %opcodes = (
+ pushl => \&op_pushl,
+ popl => \&op_popl,
+ movl => \&op_movl,
+ movzbl => \&op_movl,
+ movzwl => \&op_movl,
+ movl => \&op_movl,
+ movw => \&op_movl,
+ movb => \&op_movl,
+ xchgb => \&op_xchgb,
+ leal => \&op_leal,
+ xorl => \&op_alu2,
+ sall => \&op_alu2,
+ sarl => \&op_alu2,
+ shll => \&op_alu2,
+ shrl => \&op_alu2,
+ addl => \&op_alu2,
+ subl => \&op_alu2,
+ incl => \&op_alu1,
+ decl => \&op_alu1,
+ negl => \&op_alu1,
+ notl => \&op_alu1,
+ setg => \&op_alu1,
+ setge => \&op_alu1,
+ setl => \&op_alu1,
+ setle => \&op_alu1,
+ sete => \&op_alu1,
+ setne => \&op_alu1,
+ incb => \&op_alu1,
+ decb => \&op_alu1,
+ testl => \&op_alu2,
+ cmpl => \&op_alu2,
+ orl => \&op_alu2,
+ andl => \&op_alu2,
+ orb => \&op_alu2,
+ subb => \&op_alu2,
+ addb => \&op_alu2,
+ cmpb => \&op_alu2,
+ testb => \&op_alu2,
+ imull => \&op_imull,
+ ret => \&op_ret,
+ call => \&op_jmp,
+ jmp => \&op_jmp,
+ je => \&op_jmp,
+ jne => \&op_jmp,
+ jg => \&op_jmp,
+ jng => \&op_jmp,
+ jl => \&op_jmp,
+ jnl => \&op_jmp,
+ js => \&op_jmp,
+ jns => \&op_jmp,
+ ja => \&op_jmp,
+ jna => \&op_jmp,
+ jb => \&op_jmp,
+ jnb => \&op_jmp,
+ jbe => \&op_jmp,
+ jle => \&op_jmp,
+ jge => \&op_jmp,
+ cltd => \&op_cltd,
+ idivl => \&op_idivl,
+ );
+}
+
+
+sub op_pushl
+{
+ my ($op, $args, $val1, $arg1);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 1) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+
+ $val1 = const_eval $arg1;
+
+ $state->{'..addr'}[0] = const_eval_addr $arg1 if is_ref $arg1;
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg {$val1}";
+
+ push_on_stack $val1;
+}
+
+
+sub op_popl
+{
+ my ($op, $args, $val1, $arg1);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 1) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+
+ $val1 = const_eval_addr $arg1;
+
+ $state->{'..addr'}[0] = $val1 if is_ref $arg1;
+
+ set_state $val1, pop_from_stack;
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg {$val1}";
+}
+
+
+sub op_movl
+{
+ my ($op, $args, $val1, $val2, $arg1, $arg2, $val3);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 2) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+ $arg2 = $op->{arg}[1]->{val};
+
+ if($full_reg{$arg1}) {
+ $arg1 = $full_reg{$arg1};
+ }
+
+ if($full_reg{$arg2}) {
+ $arg2 = $full_reg{$arg2};
+ }
+
+ $val1 = const_eval $arg1;
+ $val2 = const_eval_addr $arg2;
+
+ $val3 = $val1;
+
+ if(
+ $op->{instr} eq 'movzbl' ||
+ $op->{instr} eq 'movzwl' ||
+ $op->{instr} eq 'movb' ||
+ $op->{instr} eq 'movw'
+ ) {
+ $val3 = '?';
+ }
+
+ $state->{'..addr'}[0] = const_eval_addr $arg1 if is_ref $arg1;
+ $state->{'..addr'}[1] = $val2 if is_ref $arg2;
+
+ set_state $val2, $val3;
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg1 {$val1}, $arg2 {$val2}";
+}
+
+
+sub op_leal
+{
+ my ($op, $args, $val1, $val2, $arg1, $arg2);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 2) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+ $arg2 = $op->{arg}[1]->{val};
+
+ $val1 = const_eval_addr $arg1;
+ $val2 = const_eval_addr $arg2;
+
+ set_state $val2, $val1;
+
+ # $state->{'..addr'}[0] = $val1;
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg1 {$val1}, $arg2 {$val2}";
+}
+
+
+sub op_xchgb
+{
+ my ($op, $args, $val1, $val2, $arg1, $arg2, $val3);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 2) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+ $arg2 = $op->{arg}[1]->{val};
+
+ if($full_reg{$arg1}) {
+ $arg1 = $full_reg{$arg1};
+ }
+
+ if($full_reg{$arg2}) {
+ $arg2 = $full_reg{$arg2};
+ }
+
+ $val1 = const_eval_addr $arg1;
+ $val2 = const_eval_addr $arg2;
+
+ $val3 = '?';
+
+ $state->{'..addr'}[0] = $val1 if is_ref $arg1;
+ $state->{'..addr'}[1] = $val2 if is_ref $arg2;
+
+ set_state $val1, $val3;
+ set_state $val2, $val3;
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg1 {$val1}, $arg2 {$val2}";
+}
+
+
+sub op_alu2
+{
+ my ($op, $args, $val1, $val2, $val3, $arg1, $arg2, $arg3, $no_change);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 2) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+ $arg2 = $op->{arg}[1]->{val};
+
+ if($full_reg{$arg1}) {
+ $arg1 = $full_reg{$arg1};
+ }
+
+ if($full_reg{$arg2}) {
+ $arg2 = $full_reg{$arg2};
+ }
+
+ $val1 = const_eval $arg1;
+ $val2 = const_eval $arg2;
+ $arg3 = const_eval_addr $arg2;
+
+ $val3 = '?';
+
+ if($op->{instr} eq 'sall' || $op->{instr} eq 'shll') {
+ if(is_num($val1) && is_num($val2)) {
+ $val3 = $val2 << $val1;
+ }
+ elsif($arg1 eq '0') {
+ $val3 = $val2;
+ }
+ }
+ elsif($op->{instr} eq 'sarl' || $op->{instr} eq 'shrl') {
+ if(is_num($val1) && is_num($val2)) {
+ if($val1 >= 1 && $op->{instr} eq 'shrl') {
+ $val3 = ($val2 & (1 << 31)) >> $val1;
+ }
+ else {
+ $val3 = $val2 >> $val1;
+ }
+ }
+ elsif($arg1 eq '0') {
+ $val3 = $val2;
+ }
+ }
+ elsif($op->{instr} eq 'xorl') {
+ if(is_num($val1) && is_num($val2)) {
+ $val3 = ($val1 + 0) ^ ($val2 + 0);
+ $val3 = $val3;
+ }
+ elsif($arg1 eq $arg2) {
+ $val3 = '0';
+ }
+ }
+ elsif($op->{instr} eq 'addl') {
+ if(is_num($val1) || is_num($val2)) {
+ $val3 = const_add $val1, $val2;
+ }
+ elsif($symbol{$val1} || $symbol{$val2}) {
+ $val3 = const_add $val1, $val2;
+ }
+ elsif($val1 eq '?' || $val2 eq '?') {
+ $val3 = const_add $val1, $val2;
+ }
+ }
+ elsif($op->{instr} eq 'subl') {
+ if(is_num($val1)) {
+ $val3 = const_add $val2, -$val1;
+ }
+ }
+ elsif($op->{instr} eq 'orl') {
+ if(is_num($val1) && is_num($val2)) {
+ $val3 = $val1 | $val2;
+ }
+ elsif($arg1 eq $arg2) {
+ $val3 = $val2;
+ }
+ }
+ elsif($op->{instr} eq 'andl') {
+ if(is_num($val1) && is_num($val2)) {
+ $val3 = $val1 & $val2;
+ }
+ elsif($arg1 eq $arg2) {
+ $val3 = $val2;
+ }
+ }
+ elsif($op->{instr} eq 'orb' || $op->{instr} eq 'subb' || $op->{instr} eq 'addb') {
+ }
+ elsif(
+ $op->{instr} eq 'cmpl' || $op->{instr} eq 'testl' ||
+ $op->{instr} eq 'cmpb' || $op->{instr} eq 'testb'
+ ) {
+ $no_change = 1;
+ }
+
+ $state->{'..addr'}[0] = const_eval_addr $arg1 if is_ref $arg1;
+ $state->{'..addr'}[1] = $arg3 if is_ref $arg2;
+
+ set_state $arg3, $val3 unless $no_change;
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg1 {$val1}, $arg2 {$val2}";
+}
+
+
+sub op_alu1
+{
+ my ($op, $args, $val1, $val2, $arg1, $arg2);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 1) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+
+ if($full_reg{$arg1}) {
+ $arg1 = $full_reg{$arg1};
+ }
+
+ $val1 = const_eval $arg1;
+ $arg2 = const_eval_addr $arg1;
+
+ $val2 = '?';
+
+ if($op->{instr} eq 'incl') {
+ $val2 = const_add $val1, 1;
+ }
+ elsif($op->{instr} eq 'decl') {
+ $val2 = const_add $val1, -1;
+ }
+ elsif(
+ $op->{instr} eq 'negl' ||
+ $op->{instr} eq 'notl' ||
+ $op->{instr} eq 'incb' ||
+ $op->{instr} eq 'decb' ||
+ $op->{instr} =~ /^set/
+ ) {
+ $val2 = '?';
+ }
+
+ $state->{'..addr'}[0] = $arg2 if is_ref $arg1;
+
+ set_state $arg2, $val2;
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg1 {$val1}";
+}
+
+
+sub op_imull
+{
+ my ($op, $args, $val1, $val2, $val3, $arg1, $arg2, $arg3, $arg4, $fact);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 2 && $args != 3) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ if($args == 2) {
+ $arg1 = $op->{arg}[0]->{val};
+ $arg2 = $op->{arg}[1]->{val};
+ }
+ else {
+ $arg1 = $op->{arg}[0]->{val};
+ $arg2 = $op->{arg}[1]->{val};
+ $arg3 = $op->{arg}[2]->{val};
+ }
+
+ if($full_reg{$arg1}) {
+ $arg1 = $full_reg{$arg1};
+ }
+
+ if($full_reg{$arg2}) {
+ $arg2 = $full_reg{$arg2};
+ }
+
+ $val1 = const_eval $arg1;
+ $val2 = const_eval $arg2;
+
+ if($args == 2) {
+ $arg4 = const_eval_addr $arg2;
+ }
+ else {
+ $arg4 = const_eval_addr $arg3;
+ }
+
+ $val3 = '?';
+
+ if(is_num($val1) && is_num($val2)) {
+ $val3 = $val1 * $val2;
+ }
+
+ if($args == 2) {
+ $state->{'..addr'}[0] = const_eval_addr $arg1 if is_ref $arg1;
+ $state->{'..addr'}[1] = $arg4 if is_ref $arg2;
+ }
+ else {
+ $state->{'..addr'}[0] = const_eval_addr $arg1 if is_ref $arg1;
+ $state->{'..addr'}[1] = const_eval_addr $arg2 if is_ref $arg2;
+ $state->{'..addr'}[1] = $arg4 if is_ref $arg3;
+ }
+
+ set_state $arg4, $val3;
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg1 {$val1}, $arg2 {$val2}";
+}
+
+
+sub op_cltd
+{
+ my ($op, $args);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 0) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ set_state "%edx", '?';
+
+ $state->{'..ilog'} = "$loc: $op->{instr}";
+}
+
+
+sub op_idivl
+{
+ my ($op, $args, $val1, $val2, $arg1, $arg2);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 1) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+
+ $val1 = const_eval $arg1;
+ $arg2 = const_eval_addr $arg1;
+
+ $state->{'..addr'}[0] = $arg2 if is_ref $arg1;
+
+ set_state "%eax", '?';
+ set_state "%edx", '?';
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg1 {$val1}";
+}
+
+
+sub op_ret
+{
+ my ($op, $args, $i);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ # no 'ret n' support
+ if($args != 0) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $state->{'%eip'} = pop_from_stack;
+
+ $state->{'..ilog'} = "$loc: $op->{instr}";
+}
+
+
+sub op_jmp
+{
+ my ($op, $args, $val1, $arg1, $ns, $taken);
+
+ $op = shift;
+
+ $args = @{$op->{arg}};
+
+ if($args != 1) {
+ warn "$pc: invalid number of arguments\n";
+ return;
+ }
+
+ $arg1 = $op->{arg}[0]->{val};
+
+ $val1 = const_eval $arg1;
+
+ if($symbol{$val1}) {
+ $val1 = "$symbol{$val1}{line}+%cs";
+ }
+
+ $state->{'..ilog'} = "$loc: $op->{instr} $arg {$val1}";
+
+ if($op->{instr} eq 'call') {
+ if($val1 =~ /^(${re_num})\+%cs$/ && $text{$1}{state}) {
+ $taken = 1;
+ }
+
+ # del_state_context $arg1;
+
+ # print ">> $loc: call $arg1\n";
+
+ # evil hack to avoid loops
+ if(!$taken) {
+ push_on_stack $state->{'%eip'};
+ $state->{'%eip'} = $val1;
+ }
+ }
+ else {
+ if($val1 =~ /^(${re_num})\+%cs$/ && $text{$1}{state}) {
+ $taken = 1;
+ }
+
+ if($op->{instr} eq 'jmp') {
+ # unconditional braches
+ if($taken) {
+ $state->{'..stop'} = 1;
+ # print "** $loc: stopped\n";
+ }
+ $state->{'%eip'} = $val1;
+ }
+ else {
+ # conditional branches
+
+ if(!$taken) {
+ $ns = clone_state;
+ $ns->{'%eip'} = $val1;
+
+ push @branch_states, $ns;
+ }
+ }
+ }
+}
+
+
diff --git a/jpg/conv3 b/jpg/conv3
new file mode 100755
index 0000000..43e4422
--- /dev/null
+++ b/jpg/conv3
@@ -0,0 +1,41 @@
+#! /usr/bin/perl
+
+@f = (<>);
+
+$ofs = 0;
+
+for (@f) {
+ if(/^(\S*):/) {
+ $symbol = $1;
+ next;
+ }
+
+ if(/^\s*\.space\s+(\d+),\s*(\d+)/) {
+ $symbol{$symbol} = $ofs;
+ $ofs += $1;
+ undef $_;
+ }
+}
+
+
+for (@f) {
+ if(/^(\S*):/) {
+ $symbol = $1;
+
+ if(defined $symbol{$symbol}) {
+ undef $_;
+
+ if(!$datasize) {
+ $datasize = 1;
+ $size = "\t\tjpg_data_size\tequ $ofs\n";
+ }
+
+ $_ = "\t.equ\t$symbol, $symbol{$symbol}\n"
+ }
+ next;
+ }
+}
+
+print @f;
+
+print STDERR $size;
diff --git a/jpg/getword b/jpg/getword
new file mode 100644
index 0000000..50ad07f
--- /dev/null
+++ b/jpg/getword
@@ -0,0 +1,7 @@
+getword:
+ movl datap, %edx
+ movzwl (%edx), %eax
+ addl $2, %edx
+ xchgb %al, %ah
+ movl %edx, datap
+ ret
diff --git a/jpg/jpg.c b/jpg/jpg.c
new file mode 100644
index 0000000..c4a9bac
--- /dev/null
+++ b/jpg/jpg.c
@@ -0,0 +1,948 @@
+#include "jpg.h"
+
+#define ERR_NO_SOI 1
+#define ERR_NOT_8BIT 2
+#define ERR_HEIGHT_MISMATCH 3
+#define ERR_WIDTH_MISMATCH 4
+#define ERR_BAD_WIDTH_OR_HEIGHT 5
+#define ERR_TOO_MANY_COMPPS 6
+#define ERR_ILLEGAL_HV 7
+#define ERR_QUANT_TABLE_SELECTOR 8
+#define ERR_NOT_YCBCR_221111 9
+#define ERR_UNKNOWN_CID_IN_SCAN 10
+#define ERR_NOT_SEQUENTIAL_DCT 11
+#define ERR_WRONG_MARKER 12
+#define ERR_NO_EOI 13
+#define ERR_BAD_TABLES 14
+#define ERR_DEPTH_MISMATCH 15
+
+#define ISHIFT 11
+
+#define IFIX(a) ((int)((a) * (1 << ISHIFT) + .5))
+#define IMULT(a, b) (((a) * (b)) >> ISHIFT)
+#define ITOINT(a) ((a) >> ISHIFT)
+
+#ifndef __P
+# define __P(x) x
+#endif
+
+/* special markers */
+#define M_BADHUFF -1
+#define M_EOF 0x80
+
+struct in {
+ unsigned int bits;
+ int left;
+ int marker;
+ void *data;
+};
+
+/*********************************/
+struct dec_hufftbl;
+struct enc_hufftbl;
+
+union hufftblp {
+ struct dec_hufftbl *dhuff;
+ struct enc_hufftbl *ehuff;
+};
+
+struct scan {
+ int dc; /* old dc value */
+
+ union hufftblp hudc;
+ union hufftblp huac;
+ int next; /* when to switch to next scan */
+
+ int cid; /* component id */
+ int hv; /* horiz/vert, copied from comp */
+ int tq; /* quant tbl, copied from comp */
+};
+
+/*********************************/
+
+#define DECBITS 8 /* seems to be the optimum */
+
+struct dec_hufftbl {
+ int maxcode[17];
+ int valptr[16];
+ unsigned char vals[256];
+ unsigned int llvals[1 << DECBITS];
+};
+
+struct jpeg_decdata {
+ int dcts[6 * 64 + 16];
+ int out[64 * 6];
+ int dquant[3][64];
+};
+
+static void decode_mcus __P((struct in *, int *, int, struct scan *, int *));
+static void dec_makehuff __P((struct dec_hufftbl *, int *, unsigned char *));
+
+static void setinput __P((struct in *));
+/*********************************/
+
+#undef PREC
+#define PREC int
+
+static void idctqtab __P((unsigned char *, PREC *));
+static void idct __P((int *, int *, PREC *, PREC, int));
+static void scaleidctqtab __P((PREC *, PREC));
+
+/*********************************/
+
+static void initcol __P((PREC[][64]));
+
+static void col221111(int *out, unsigned char *pic, int width, int bits);
+static unsigned char tmp_img[16*16*4]; /* 16 x 16, 32 bit color */
+
+/*********************************/
+
+#define M_SOI 0xd8
+#define M_APP0 0xe0
+#define M_DQT 0xdb
+#define M_SOF0 0xc0
+#define M_DHT 0xc4
+#define M_DRI 0xdd
+#define M_SOS 0xda
+#define M_RST0 0xd0
+#define M_EOI 0xd9
+#define M_COM 0xfe
+
+static unsigned char *datap;
+static struct jpeg_decdata decdata;
+
+static void memset(void *p, int c, int n)
+{
+ unsigned char *x = p;
+
+ while(n--) *x++ = c;
+}
+
+
+static int getbyte(void)
+{
+ return *datap++;
+}
+
+static int getword(void)
+{
+ int c1, c2;
+ c1 = *datap;
+ c2 = datap[1];
+ datap += 2;
+ return c1 << 8 | c2;
+}
+
+struct comp {
+ int cid;
+ int hv;
+ int tq;
+};
+
+#define MAXCOMP 4
+struct jpginfo {
+ int nc; /* number of components */
+ int ns; /* number of scans */
+ int dri; /* restart interval */
+};
+
+static struct jpginfo info;
+static struct comp comps[MAXCOMP];
+
+static struct scan dscans[MAXCOMP];
+
+static unsigned char quant[4][64];
+
+static struct dec_hufftbl dhuff[4];
+
+#define dec_huffdc (dhuff + 0)
+#define dec_huffac (dhuff + 2)
+
+static struct in in;
+
+static int readtables(int till)
+{
+ int m, l, i, j, lq, pq, tq;
+ int tc, th, tt;
+
+ for (;;) {
+ if (getbyte() != 0xff)
+ return -1;
+ if ((m = getbyte()) == till)
+ break;
+
+ switch (m) {
+ case 0xc2:
+ return 0;
+
+ case M_DQT:
+ lq = getword();
+ while (lq > 2) {
+ pq = getbyte();
+ tq = pq & 15;
+ if (tq > 3)
+ return -1;
+ pq >>= 4;
+ if (pq != 0)
+ return -1;
+ for (i = 0; i < 64; i++)
+ quant[tq][i] = getbyte();
+ lq -= 64 + 1;
+ }
+ break;
+
+ case M_DHT:
+ l = getword();
+ while (l > 2) {
+ int hufflen[16], k;
+ unsigned char huffvals[256];
+
+ tc = getbyte();
+ th = tc & 15;
+ tc >>= 4;
+ tt = tc * 2 + th;
+ if (tc > 1 || th > 1)
+ return -1;
+ for (i = 0; i < 16; i++)
+ hufflen[i] = getbyte();
+ l -= 1 + 16;
+ k = 0;
+ for (i = 0; i < 16; i++) {
+ for (j = 0; j < hufflen[i]; j++)
+ huffvals[k++] = getbyte();
+ l -= hufflen[i];
+ }
+ dec_makehuff(dhuff + tt, hufflen,
+ huffvals);
+ }
+ break;
+
+ case M_DRI:
+ l = getword();
+ info.dri = getword();
+ break;
+
+ default:
+ l = getword();
+ while (l-- > 2)
+ getbyte();
+ break;
+ }
+ }
+ return 0;
+}
+
+static void dec_initscans(void)
+{
+ int i;
+
+ for (i = 0; i < info.ns; i++)
+ dscans[i].dc = 0;
+}
+
+int jpeg_decode(unsigned char *buf, unsigned char *pic, int x0, int x1, int y0, int y1, int color_bits)
+{
+ int i, j, m, tac, tdc;
+ int mcusx, mcusy, mx, my;
+ int max[6];
+ int width, height;
+ int mx0, mx1, my0, my1;
+
+ datap = buf;
+ if (getbyte() != 0xff)
+ return ERR_NO_SOI;
+ if (getbyte() != M_SOI)
+ return ERR_NO_SOI;
+ if (readtables(M_SOF0))
+ return ERR_BAD_TABLES;
+ if(info.dri) return ERR_WRONG_MARKER;
+
+ getword();
+ i = getbyte();
+ if (i != 8)
+ return ERR_NOT_8BIT;
+
+ height = getword();
+ width = getword();
+
+ info.nc = getbyte();
+ if (info.nc > MAXCOMP)
+ return ERR_TOO_MANY_COMPPS;
+ for (i = 0; i < info.nc; i++) {
+ int h, v;
+ comps[i].cid = getbyte();
+ comps[i].hv = getbyte();
+ v = comps[i].hv & 15;
+ h = comps[i].hv >> 4;
+ comps[i].tq = getbyte();
+ if (h > 3 || v > 3)
+ return ERR_ILLEGAL_HV;
+ if (comps[i].tq > 3)
+ return ERR_QUANT_TABLE_SELECTOR;
+ }
+ if (readtables(M_SOS))
+ return ERR_BAD_TABLES;
+ getword();
+ info.ns = getbyte();
+ if (info.ns != 3)
+ return ERR_NOT_YCBCR_221111;
+ for (i = 0; i < 3; i++) {
+ dscans[i].cid = getbyte();
+ tdc = getbyte();
+ tac = tdc & 15;
+ tdc >>= 4;
+ if (tdc > 1 || tac > 1)
+ return ERR_QUANT_TABLE_SELECTOR;
+ for (j = 0; j < info.nc; j++)
+ if (comps[j].cid == dscans[i].cid)
+ break;
+ if (j == info.nc)
+ return ERR_UNKNOWN_CID_IN_SCAN;
+ dscans[i].hv = comps[j].hv;
+ dscans[i].tq = comps[j].tq;
+ dscans[i].hudc.dhuff = dec_huffdc + tdc;
+ dscans[i].huac.dhuff = dec_huffac + tac;
+ }
+
+ i = getbyte();
+ j = getbyte();
+ m = getbyte();
+
+ if (i != 0 || j != 63 || m != 0)
+ return ERR_NOT_SEQUENTIAL_DCT;
+
+ if (dscans[0].cid != 1 || dscans[1].cid != 2 || dscans[2].cid != 3)
+ return ERR_NOT_YCBCR_221111;
+
+ if (dscans[0].hv != 0x22 || dscans[1].hv != 0x11 || dscans[2].hv != 0x11)
+ return ERR_NOT_YCBCR_221111;
+
+ mcusx = (width + 15) >> 4;
+ mcusy = (height + 15) >> 4;
+
+ mx0 = x0 >> 4;
+ my0 = y0 >> 4;
+
+ /* inclusive! */
+ mx1 = ((x1 + 15) >> 4) - 1;
+ my1 = ((y1 + 15) >> 4) - 1;
+
+ if(my1 < mcusy) mcusy = my1 + 1;
+
+ idctqtab(quant[dscans[0].tq], decdata.dquant[0]);
+ idctqtab(quant[dscans[1].tq], decdata.dquant[1]);
+ idctqtab(quant[dscans[2].tq], decdata.dquant[2]);
+ initcol(decdata.dquant);
+ setinput(&in);
+
+ dec_initscans();
+
+ dscans[0].next = 6 - 4;
+ dscans[1].next = 6 - 4 - 1;
+ dscans[2].next = 6 - 4 - 1 - 1; /* 411 encoding */
+ for (my = 0; my < mcusy; my++) {
+ for (mx = 0; mx < mcusx; mx++) {
+ decode_mcus(&in, decdata.dcts, 6, dscans, max);
+
+ if(
+ my >= my0 && my <= my1 &&
+ mx >= mx0 && mx <= mx1
+ ) {
+ int i0, i1, j0, j1, yofs;
+
+ idct(decdata.dcts, decdata.out, decdata.dquant[0], IFIX(128.5), max[0]);
+ idct(decdata.dcts + 64, decdata.out + 64, decdata.dquant[0], IFIX(128.5), max[1]);
+ idct(decdata.dcts + 128, decdata.out + 128, decdata.dquant[0], IFIX(128.5), max[2]);
+ idct(decdata.dcts + 192, decdata.out + 192, decdata.dquant[0], IFIX(128.5), max[3]);
+ idct(decdata.dcts + 256, decdata.out + 256, decdata.dquant[1], IFIX(0.5), max[4]);
+ idct(decdata.dcts + 320, decdata.out + 320, decdata.dquant[2], IFIX(0.5), max[5]);
+
+ // color_bits * 2: actually 16 * (color_bits / 8)
+ col221111(decdata.out, tmp_img, color_bits * 2, color_bits);
+
+ j0 = my == my0 ? y0 - 16 * my : 0;
+ j1 = my == my1 ? y1 - 16 * my : 16;
+ for(j = j0; j < j1; j++) {
+ yofs = (16 * my - y0 + j) * (x1 - x0);
+ i0 = mx == mx0 ? x0 - 16 * mx : 0;
+ i1 = mx == mx1 ? x1 - 16 * mx : 16;
+
+ switch(color_bits) {
+ case 8:
+ for(i = i0; i < i1; i++) {
+ *((unsigned char *) pic + 16 * mx - x0 + i + yofs) =
+ *((unsigned char *) tmp_img + 16 * j + i);
+ }
+ break;
+
+ case 16:
+ for(i = i0; i < i1; i++) {
+ *((unsigned short *) pic + 16 * mx - x0 + i + yofs) =
+ *((unsigned short *) tmp_img + 16 * j + i);
+ }
+ break;
+
+ case 32:
+ for(i = i0; i < i1; i++) {
+ *((unsigned *) pic + 16 * mx - x0 + i + yofs) =
+ *((unsigned *) tmp_img + 16 * j + i);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/****************************************************************/
+/************** huffman decoder ***************/
+/****************************************************************/
+
+static int fillbits __P((struct in *, int, unsigned int));
+static int dec_rec2
+__P((struct in *, struct dec_hufftbl *, int *, int, int));
+
+static void setinput(in)
+struct in *in;
+{
+ in->left = 0;
+ in->bits = 0;
+ in->marker = 0;
+}
+
+static int fillbits(in, le, bi)
+struct in *in;
+int le;
+unsigned int bi;
+{
+ int b, m;
+
+ if (in->marker) {
+ if (le <= 16)
+ in->bits = bi << 16, le += 16;
+ return le;
+ }
+ while (le <= 24) {
+ b = getbyte();
+ if (b == 0xff && (m = getbyte()) != 0) {
+ in->marker = m;
+ if (le <= 16)
+ bi = bi << 16, le += 16;
+ break;
+ }
+ bi = bi << 8 | b;
+ le += 8;
+ }
+ in->bits = bi; /* tmp... 2 return values needed */
+ return le;
+}
+
+#define LEBI_DCL int le, bi
+#define LEBI_GET(in) (le = in->left, bi = in->bits)
+#define LEBI_PUT(in) (in->left = le, in->bits = bi)
+
+#define GETBITS(in, n) ( \
+ (le < (n) ? le = fillbits(in, le, bi), bi = in->bits : 0), \
+ (le -= (n)), \
+ bi >> le & ((1 << (n)) - 1) \
+)
+
+#define UNGETBITS(in, n) ( \
+ le += (n) \
+)
+
+
+static int dec_rec2(in, hu, runp, c, i)
+struct in *in;
+struct dec_hufftbl *hu;
+int *runp;
+int c, i;
+{
+ LEBI_DCL;
+
+ LEBI_GET(in);
+ if (i) {
+ UNGETBITS(in, i & 127);
+ *runp = i >> 8 & 15;
+ i >>= 16;
+ } else {
+ for (i = DECBITS; (c = ((c << 1) | GETBITS(in, 1))) >= (hu->maxcode[i]); i++);
+ if (i >= 16) {
+ in->marker = M_BADHUFF;
+ return 0;
+ }
+ i = hu->vals[hu->valptr[i] + c - hu->maxcode[i - 1] * 2];
+ *runp = i >> 4;
+ i &= 15;
+ }
+ if (i == 0) { /* sigh, 0xf0 is 11 bit */
+ LEBI_PUT(in);
+ return 0;
+ }
+ /* receive part */
+ c = GETBITS(in, i);
+ if (c < (1 << (i - 1)))
+ c += (-1 << i) + 1;
+ LEBI_PUT(in);
+ return c;
+}
+
+#define DEC_REC(in, hu, r, i) ( \
+ r = GETBITS(in, DECBITS), \
+ i = hu->llvals[r], \
+ i & 128 ? \
+ ( \
+ UNGETBITS(in, i & 127), \
+ r = i >> 8 & 15, \
+ i >> 16 \
+ ) \
+ : \
+ ( \
+ LEBI_PUT(in), \
+ i = dec_rec2(in, hu, &r, r, i), \
+ LEBI_GET(in), \
+ i \
+ ) \
+)
+
+static void decode_mcus(in, dct, n, sc, maxp)
+struct in *in;
+int *dct;
+int n;
+struct scan *sc;
+int *maxp;
+{
+ struct dec_hufftbl *hu;
+ int i, r, t;
+ LEBI_DCL;
+
+ memset(dct, 0, n * 64 * sizeof(*dct));
+ LEBI_GET(in);
+ while (n-- > 0) {
+ hu = sc->hudc.dhuff;
+ *dct++ = (sc->dc += DEC_REC(in, hu, r, t));
+
+ hu = sc->huac.dhuff;
+ i = 63;
+ while (i > 0) {
+ t = DEC_REC(in, hu, r, t);
+ if (t == 0 && r == 0) {
+ dct += i;
+ break;
+ }
+ dct += r;
+ *dct++ = t;
+ i -= r + 1;
+ }
+ *maxp++ = 64 - i;
+ if (n == sc->next)
+ sc++;
+ }
+ LEBI_PUT(in);
+}
+
+static void dec_makehuff(hu, hufflen, huffvals)
+struct dec_hufftbl *hu;
+int *hufflen;
+unsigned char *huffvals;
+{
+ int code, k, i, j, d, x, c, v;
+ for (i = 0; i < (1 << DECBITS); i++)
+ hu->llvals[i] = 0;
+
+/*
+ * llvals layout:
+ *
+ * value v already known, run r, backup u bits:
+ * vvvvvvvvvvvvvvvv 0000 rrrr 1 uuuuuuu
+ * value unknown, size b bits, run r, backup u bits:
+ * 000000000000bbbb 0000 rrrr 0 uuuuuuu
+ * value and size unknown:
+ * 0000000000000000 0000 0000 0 0000000
+ */
+ code = 0;
+ k = 0;
+ for (i = 0; i < 16; i++, code <<= 1) { /* sizes */
+ hu->valptr[i] = k;
+ for (j = 0; j < hufflen[i]; j++) {
+ hu->vals[k] = *huffvals++;
+ if (i < DECBITS) {
+ c = code << (DECBITS - 1 - i);
+ v = hu->vals[k] & 0x0f; /* size */
+ for (d = 1 << (DECBITS - 1 - i); --d >= 0;) {
+ if (v + i < DECBITS) { /* both fit in table */
+ x = d >> (DECBITS - 1 - v -
+ i);
+ if (v && x < (1 << (v - 1)))
+ x += (-1 << v) + 1;
+ x = x << 16 | (hu-> vals[k] & 0xf0) << 4 |
+ (DECBITS - (i + 1 + v)) | 128;
+ } else
+ x = v << 16 | (hu-> vals[k] & 0xf0) << 4 |
+ (DECBITS - (i + 1));
+ hu->llvals[c | d] = x;
+ }
+ }
+ code++;
+ k++;
+ }
+ hu->maxcode[i] = code;
+ }
+ hu->maxcode[16] = 0x20000; /* always terminate decode */
+}
+
+/****************************************************************/
+/************** idct ***************/
+/****************************************************************/
+
+#define ONE ((PREC)IFIX(1.))
+#define S2 ((PREC)IFIX(0.382683432))
+#define C2 ((PREC)IFIX(0.923879532))
+#define C4 ((PREC)IFIX(0.707106781))
+
+#define S22 ((PREC)IFIX(2 * 0.382683432))
+#define C22 ((PREC)IFIX(2 * 0.923879532))
+#define IC4 ((PREC)IFIX(1 / 0.707106781))
+
+#define C3IC1 ((PREC)IFIX(0.847759065)) /* c3/c1 */
+#define C5IC1 ((PREC)IFIX(0.566454497)) /* c5/c1 */
+#define C7IC1 ((PREC)IFIX(0.198912367)) /* c7/c1 */
+
+#define XPP(a,b) (t = a + b, b = a - b, a = t)
+#define XMP(a,b) (t = a - b, b = a + b, a = t)
+#define XPM(a,b) (t = a + b, b = b - a, a = t)
+
+#define ROT(a,b,s,c) ( t = IMULT(a + b, s), \
+ a = IMULT(a, c - s) + t, \
+ b = IMULT(b, c + s) - t)
+
+#define IDCT \
+( \
+ XPP(t0, t1), \
+ XMP(t2, t3), \
+ t2 = IMULT(t2, IC4) - t3, \
+ XPP(t0, t3), \
+ XPP(t1, t2), \
+ XMP(t4, t7), \
+ XPP(t5, t6), \
+ XMP(t5, t7), \
+ t5 = IMULT(t5, IC4), \
+ ROT(t4, t6, S22, C22),\
+ t6 -= t7, \
+ t5 -= t6, \
+ t4 -= t5, \
+ XPP(t0, t7), \
+ XPP(t1, t6), \
+ XPP(t2, t5), \
+ XPP(t3, t4) \
+)
+
+static unsigned char zig2[64] = {
+ 0, 2, 3, 9, 10, 20, 21, 35,
+ 14, 16, 25, 31, 39, 46, 50, 57,
+ 5, 7, 12, 18, 23, 33, 37, 48,
+ 27, 29, 41, 44, 52, 55, 59, 62,
+ 15, 26, 30, 40, 45, 51, 56, 58,
+ 1, 4, 8, 11, 19, 22, 34, 36,
+ 28, 42, 43, 53, 54, 60, 61, 63,
+ 6, 13, 17, 24, 32, 38, 47, 49
+};
+
+void idct(in, out, quant, off, max)
+int *in;
+int *out;
+PREC *quant;
+PREC off;
+int max;
+{
+ PREC t0, t1, t2, t3, t4, t5, t6, t7, t;
+ PREC tmp[64], *tmpp;
+ int i, j;
+ unsigned char *zig2p;
+
+ t0 = off;
+ if (max == 1) {
+ t0 += in[0] * quant[0];
+ for (i = 0; i < 64; i++)
+ out[i] = ITOINT(t0);
+ return;
+ }
+ zig2p = zig2;
+ tmpp = tmp;
+ for (i = 0; i < 8; i++) {
+ j = *zig2p++;
+ t0 += in[j] * quant[j];
+ j = *zig2p++;
+ t5 = in[j] * quant[j];
+ j = *zig2p++;
+ t2 = in[j] * quant[j];
+ j = *zig2p++;
+ t7 = in[j] * quant[j];
+ j = *zig2p++;
+ t1 = in[j] * quant[j];
+ j = *zig2p++;
+ t4 = in[j] * quant[j];
+ j = *zig2p++;
+ t3 = in[j] * quant[j];
+ j = *zig2p++;
+ t6 = in[j] * quant[j];
+ IDCT;
+ tmpp[0 * 8] = t0;
+ tmpp[1 * 8] = t1;
+ tmpp[2 * 8] = t2;
+ tmpp[3 * 8] = t3;
+ tmpp[4 * 8] = t4;
+ tmpp[5 * 8] = t5;
+ tmpp[6 * 8] = t6;
+ tmpp[7 * 8] = t7;
+ tmpp++;
+ t0 = 0;
+ }
+ for (i = 0; i < 8; i++) {
+ t0 = tmp[8 * i + 0];
+ t1 = tmp[8 * i + 1];
+ t2 = tmp[8 * i + 2];
+ t3 = tmp[8 * i + 3];
+ t4 = tmp[8 * i + 4];
+ t5 = tmp[8 * i + 5];
+ t6 = tmp[8 * i + 6];
+ t7 = tmp[8 * i + 7];
+ IDCT;
+ out[8 * i + 0] = ITOINT(t0);
+ out[8 * i + 1] = ITOINT(t1);
+ out[8 * i + 2] = ITOINT(t2);
+ out[8 * i + 3] = ITOINT(t3);
+ out[8 * i + 4] = ITOINT(t4);
+ out[8 * i + 5] = ITOINT(t5);
+ out[8 * i + 6] = ITOINT(t6);
+ out[8 * i + 7] = ITOINT(t7);
+ }
+}
+
+static unsigned char zig[64] = {
+ 0, 1, 5, 6, 14, 15, 27, 28,
+ 2, 4, 7, 13, 16, 26, 29, 42,
+ 3, 8, 12, 17, 25, 30, 41, 43,
+ 9, 11, 18, 24, 31, 40, 44, 53,
+ 10, 19, 23, 32, 39, 45, 52, 54,
+ 20, 22, 33, 38, 46, 51, 55, 60,
+ 21, 34, 37, 47, 50, 56, 59, 61,
+ 35, 36, 48, 49, 57, 58, 62, 63
+};
+
+static PREC aaidct[8] = {
+ IFIX(0.3535533906), IFIX(0.4903926402),
+ IFIX(0.4619397663), IFIX(0.4157348062),
+ IFIX(0.3535533906), IFIX(0.2777851165),
+ IFIX(0.1913417162), IFIX(0.0975451610)
+};
+
+
+static void idctqtab(qin, qout)
+unsigned char *qin;
+PREC *qout;
+{
+ int i, j;
+
+ for (i = 0; i < 8; i++)
+ for (j = 0; j < 8; j++)
+ qout[zig[i * 8 + j]] = qin[zig[i * 8 + j]] *
+ IMULT(aaidct[i], aaidct[j]);
+}
+
+static void scaleidctqtab(q, sc)
+PREC *q;
+PREC sc;
+{
+ int i;
+
+ for (i = 0; i < 64; i++)
+ q[i] = IMULT(q[i], sc);
+}
+
+/****************************************************************/
+/************** color decoder ***************/
+/****************************************************************/
+
+/*
+ * YCbCr Color transformation:
+ *
+ * y:0..255 Cb:-128..127 Cr:-128..127
+ *
+ * R = Y + 1.40200 * Cr
+ * G = Y - 0.34414 * Cb - 0.71414 * Cr
+ * B = Y + 1.77200 * Cb
+ *
+ * =>
+ * Cr *= 1.40200;
+ * Cb *= 1.77200;
+ * Cg = 0.19421 * Cb + .50937 * Cr;
+ * R = Y + Cr;
+ * G = Y - Cg;
+ * B = Y + Cb;
+ *
+ * =>
+ * Cg = (50 * Cb + 130 * Cr + 128) >> 8;
+ */
+
+static void initcol(q)
+PREC q[][64];
+{
+ scaleidctqtab(q[1], IFIX(1.77200));
+ scaleidctqtab(q[2], IFIX(1.40200));
+}
+
+/* This is optimized for the stupid sun SUNWspro compiler. */
+#define STORECLAMP(a,x) \
+( \
+ (a) = (x), \
+ (unsigned int)(x) >= 256 ? \
+ ((a) = (x) < 0 ? 0 : 255) \
+ : \
+ 0 \
+)
+
+#define CLAMP(x) ((unsigned int)(x) >= 256 ? ((x) < 0 ? 0 : 255) : (x))
+
+#define CBCRCG(xin) \
+( \
+ cb = outc[0 + xin], \
+ cr = outc[64 + xin], \
+ cg = (50 * cb + 130 * cr + 128) >> 8 \
+)
+
+#define PIC(yin, xin, p, xout) \
+( \
+ y = outy[(yin) * 8 + xin], \
+ STORECLAMP(p[(xout) * 4 + 0], y + cb), \
+ STORECLAMP(p[(xout) * 4 + 1], y - cg), \
+ STORECLAMP(p[(xout) * 4 + 2], y + cr) \
+)
+
+#define PIC221111x(xin, xin_4, xin_3) \
+( \
+ CBCRCG(xin), \
+ PIC(xin_4 + 0, xin_3 + 0, pic0, xin * 2 + 0), \
+ PIC(xin_4 + 0, xin_3 + 1, pic0, xin * 2 + 1), \
+ PIC(xin_4 + 1, xin_3 + 0, pic1, xin * 2 + 0), \
+ PIC(xin_4 + 1, xin_3 + 1, pic1, xin * 2 + 1) \
+)
+
+#define PIC_16(yin, xin, p, xout, add) \
+( \
+ y = outy[(yin) * 8 + xin], \
+ *(unsigned short *) (p + (xout) * 2) = store_16(CLAMP(y + cr), CLAMP(y - cg), CLAMP(y + cb), add) \
+)
+
+#define PIC221111_16x(xin, xin_4, xin_3) \
+( \
+ CBCRCG(xin), \
+ PIC_16(xin_4 + 0, xin_3 + 0, pic0, xin * 2 + 0, 3 * 0x55), \
+ PIC_16(xin_4 + 0, xin_3 + 1, pic0, xin * 2 + 1, 0 * 0x55), \
+ PIC_16(xin_4 + 1, xin_3 + 0, pic1, xin * 2 + 0, 1 * 0x55), \
+ PIC_16(xin_4 + 1, xin_3 + 1, pic1, xin * 2 + 1, 2 * 0x55) \
+)
+
+#define PIC_8(yin, xin, p, xout, add) \
+( \
+ y = outy[(yin) * 8 + xin], \
+ p[(xout)] = store_8(CLAMP(y + cr), CLAMP(y - cg), CLAMP(y + cb), add) \
+)
+
+#define PIC221111_8x(xin, xin_4, xin_3) \
+( \
+ CBCRCG(xin), \
+ PIC_8(xin_4 + 0, xin_3 + 0, pic0, xin * 2 + 0, 3 * 0x55), \
+ PIC_8(xin_4 + 0, xin_3 + 1, pic0, xin * 2 + 1, 0 * 0x55), \
+ PIC_8(xin_4 + 1, xin_3 + 0, pic1, xin * 2 + 0, 1 * 0x55), \
+ PIC_8(xin_4 + 1, xin_3 + 1, pic1, xin * 2 + 1, 2 * 0x55) \
+)
+
+
+static unsigned store_16(unsigned r, unsigned g, unsigned b, unsigned add)
+{
+ unsigned rgb;
+
+ rgb = (((r << 5) - r + add) >> 8) << 11;
+ rgb += (((g << 6) - g + add) >> 8) << 5;
+ rgb += ((b << 5) - b + add) >> 8;
+
+ return rgb;
+}
+
+static unsigned store_8(unsigned r, unsigned g, unsigned b, unsigned add)
+{
+ unsigned rgb;
+
+#if 0
+ r = ((r << 2) - r + add) >> 8;
+ g = ((g << 3) - g + add) >> 8;
+ b = ((b << 3) - b + add) >> 8;
+
+ rgb = (r << 6) + (g << 3) + b;
+#endif
+
+ rgb = (((r << 2) - r + add) >> 8) << 6;
+ rgb += (((g << 3) - g + add) >> 8) << 3;
+ rgb += ((b << 3) - b + add) >> 8;
+
+ return rgb;
+}
+
+static void col221111(int *out, unsigned char *pic, int width, int bits)
+{
+ int i, j, k, k_4, k_3;
+ unsigned char *pic0, *pic1;
+ int *outy, *outc;
+ int cr, cg, cb, y;
+
+ pic0 = pic;
+ pic1 = pic + width;
+ outy = out;
+ outc = out + 64 * 4;
+ for(i = 2; i > 0; i--) {
+ for(j = 4; j > 0; j--) {
+ for(k = 0; k < 8; k++) {
+ k_4 = (k >> 2) << 3;
+ k_3 = (k & 3) << 1;
+ switch(bits) {
+ case 8:
+ PIC221111_8x(k, k_4, k_3);
+ break;
+ case 16:
+ PIC221111_16x(k, k_4, k_3);
+ break;
+ case 32:
+ PIC221111x(k, k_4, k_3);
+ break;
+ }
+ }
+ outc += 8;
+ outy += 16;
+ pic0 += 2 * width;
+ pic1 += 2 * width;
+ }
+ outy += 64 * 2 - 16 * 4;
+ }
+}
+
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+unsigned jpeg_get_size(unsigned char *buf)
+{
+ unsigned u;
+
+ datap = buf;
+ getbyte(); getbyte();
+ if(readtables(M_SOF0)) return 0;
+ getword(); getbyte();
+
+ u = getword() << 16;
+ u += getword();
+
+ return u;
+}
+
diff --git a/jpg/jpg.h b/jpg/jpg.h
new file mode 100644
index 0000000..b730943
--- /dev/null
+++ b/jpg/jpg.h
@@ -0,0 +1,2 @@
+int jpeg_decode(unsigned char *jpg, unsigned char *img, int x_0, int x_1, int y_0, int y_1, int color_bits);
+unsigned jpeg_get_size(unsigned char *buf);
diff --git a/jpg/test1.jpg b/jpg/test1.jpg
new file mode 100644
index 0000000..1a69e73
--- /dev/null
+++ b/jpg/test1.jpg
Binary files differ
diff --git a/jpg/test2.jpg b/jpg/test2.jpg
new file mode 100644
index 0000000..650bfaf
--- /dev/null
+++ b/jpg/test2.jpg
Binary files differ
diff --git a/jpg/test3.jpg b/jpg/test3.jpg
new file mode 100644
index 0000000..28c2773
--- /dev/null
+++ b/jpg/test3.jpg
Binary files differ
diff --git a/jpg/test4.jpg b/jpg/test4.jpg
new file mode 100644
index 0000000..c889f3b
--- /dev/null
+++ b/jpg/test4.jpg
Binary files differ
diff --git a/jpg/z2.c b/jpg/z2.c
new file mode 100644
index 0000000..a7c7741
--- /dev/null
+++ b/jpg/z2.c
@@ -0,0 +1,160 @@
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "jpg.h"
+
+int main(int argc, char **argv)
+{
+ int fd, i, width, height;
+ unsigned char *jpg = malloc(1 << 20);
+ unsigned char *img, *p;
+ unsigned char pixel[3];
+ unsigned x, z;
+ unsigned x0, x1, y0, y1;
+ unsigned bits = 0;
+
+ if(argc < 4) return 1;
+
+ if(!strcmp(argv[1], "--8")) {
+ bits = 8;
+ }
+
+ if(!strcmp(argv[1], "--16")) {
+ bits = 16;
+ }
+
+ if(!strcmp(argv[1], "--24")) {
+ bits = 24;
+ }
+
+ if(!strcmp(argv[1], "--32")) {
+ bits = 32;
+ }
+
+ if(!bits) return 10;
+
+ fd = open(argv[2], O_RDONLY);
+
+ if(fd == -1) return 2;
+
+ i = read(fd, jpg, 1 << 20);
+
+ if(i < 0) return 3;
+
+ close(fd);
+
+ jpg = realloc(jpg, i);
+
+ x = jpeg_get_size(jpg);
+
+ width = x & 0xffff;
+ height = x >> 16;
+
+ fprintf(stderr, "size = %d bytes, width = %d, height = %d\n", i, width, height);
+
+ img = malloc(width * height * 4);
+
+ x0 = 0;
+ x1 = width;
+ y0 = 0;
+ y1 = height;
+
+ i = jpeg_decode(jpg, img, x0, x1, y0, y1, bits);
+
+ width = x1 - x0;
+ height = y1 - y0;
+
+ fprintf(stderr, "decode = %d\n", i);
+
+ fprintf(stderr,
+ "%02x %02x %02x %02x %02x %02x %02x %02x\n",
+ img[0], img[1], img[2], img[3],
+ img[4], img[5], img[6], img[7]
+ );
+
+ if(argc >= 3) {
+
+ fd = open(argv[3], O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+ if(fd >= 0) {
+ char *s = NULL;
+
+ i = asprintf(&s, "P6\n%d %d\n255\n", width, height);
+ if(i > 0) write(fd, s, i);
+
+ free(s);
+
+ switch(bits) {
+ case 8:
+ for(i = 0, p = img; i < width * height; i++) {
+ x = *p++;
+ // 2 3 3
+
+ z = x >> (4 + 2);
+ pixel[0] = z * 0x55;
+
+ z = (x >> 3) & 0x07;
+ pixel[1] = z * 0x24 + (z >> 1);
+
+ z = x & 0x7;
+ pixel[2] = z * 0x24 + (z >> 1);
+
+ write(fd, &pixel, 3);
+ }
+ break;
+
+ case 16:
+ for(i = 0, p = img; i < width * height; i++) {
+ x = p[0] + (p[1] << 8);
+ p += 2;
+
+ // 5 6 5
+
+ pixel[0] = ((x >> 11) & 0x1f) << 3;
+ pixel[0] += pixel[0] >> 5;
+ pixel[1] = ((x >> 5) & 0x3f) << 2;
+ pixel[1] += pixel[1] >> 6;
+ pixel[2] = (x & 0x1f) << 3;
+ pixel[2] += pixel[2] >> 5;
+
+ write(fd, &pixel, 3);
+ }
+ break;
+
+ case 24:
+ for(i = 0, p = img; i < width * height; i++) {
+ pixel[2] = *p++;
+ pixel[1] = *p++;
+ pixel[0] = *p++;
+
+ write(fd, &pixel, 3);
+ }
+ break;
+
+ case 32:
+ for(i = 0, p = img; i < width * height; i++) {
+ pixel[2] = *p++;
+ pixel[1] = *p++;
+ pixel[0] = *p++;
+ p++;
+
+ write(fd, &pixel, 3);
+ }
+ break;
+
+ }
+
+ close(fd);
+ }
+ }
+
+ return 0;
+}
+
+