source: trunk/lib/motifHack/convert_xpm.pl

Last change on this file was 13817, checked in by westram, 10 years ago
  • avoid a race condition (occurring on new build server)
  • Property svn:executable set to *
File size: 7.6 KB
Line 
1#!/usr/bin/perl
2# ============================================================= #
3#                                                               #
4#   File      : convert_xpm.pl                                  #
5#   Purpose   : fix those .xpm that make motif fail             #
6#                                                               #
7#   Coded by Ralf Westram (coder@reallysoft.de) in March 2013   #
8#   Institute of Microbiology (Technical University Munich)     #
9#   http://www.arb-home.de/                                     #
10#                                                               #
11# ============================================================= #
12
13# Reads xpm from STDIN.
14# Writes fixed xpm to passed file (automatically creates target directories)
15#
16# Fixes done:
17# - eliminates odd x- and y-sizes by reducing or
18#   increasing the xpms size.
19#   Only transparent pixel data will be removed.
20# - removes additional comments
21#
22# see README for why this is done
23
24
25use strict;
26use warnings;
27
28sub findTransparentColor(\@$) {
29  my ($colors_r,$charsPerPixel) = @_;
30  foreach (@$colors_r) {
31    if (/c\s+none$/oi) {
32      my $t = substr($_,0,$charsPerPixel);
33      length($t) == $charsPerPixel || die;
34      return $t;
35    }
36  }
37  return undef;
38}
39
40sub transpose(\@$) {
41  my ($pixels_r,$charsPerPixel) = @_;
42  my @transposed = ();
43
44  foreach (@$pixels_r) {
45    my $org = $_;
46    for (my $i = 0; $_ ne ''; ++$i) {
47      $transposed[$i] .= substr($_,0,$charsPerPixel);
48      $_ = substr($_,$charsPerPixel);
49    }
50  }
51  @$pixels_r = @transposed;
52}
53
54sub createUniRow($$) {
55  my ($pixel,$count) = @_;
56  my $row = '';
57  while ($count--) { $row .= $pixel; }
58  return $row;
59}
60sub countUniRows($\@) {
61  my ($unirow,$rows_r) = @_;
62  my ($start,$end) = (0,0);
63  my $rows = scalar(@$rows_r);
64  for (my $i=0; $i<$rows; ++$i) {
65    if ($$rows_r[$i] eq $unirow) {
66      if ($start==$i) { $start++; }
67      $end++;
68    }
69    else {
70      $end = 0;
71    }
72  }
73
74  return ($start,$end);
75}
76sub findMostUsedPixel(\@$) {
77  my ($pixels_r,$charsPerPixel) = @_;
78  my %count = ();
79  foreach (@$pixels_r) {
80    my $row = $_;
81    while ($row ne '') {
82      my $pixel = substr($row,0,$charsPerPixel);
83      $row = substr($row,$charsPerPixel);
84      if (defined $count{$pixel}) { $count{$pixel}++; }
85      else { $count{$pixel} = 1; }
86    }
87  }
88  my $maxCount = 0;
89  my $mostUsedColor = undef;
90  foreach (keys %count) {
91    if ($count{$_} > $maxCount) {
92      $maxCount = $count{$_};
93      $mostUsedColor = $_;
94    }
95  }
96
97  defined $mostUsedColor || die;
98  return $mostUsedColor;
99}
100
101sub fixOddSize(\$\@$$) {
102  my ($ysize_r,$pixels_r,$charsPerPixel,$transparent) = @_;
103  ($$ysize_r % 2) || die;
104
105  my $xsize = length($$pixels_r[0]) / $charsPerPixel;
106  my $fixed = 0;
107
108  # check for transparent rows and drop them
109  if (defined $transparent) {
110    my $unirow = createUniRow($transparent,$xsize);
111    my ($uniStart,$uniEnd) = countUniRows($unirow,@$pixels_r);
112
113    if ($uniStart or $uniEnd) {
114      if ($uniStart>$uniEnd) { shift @$pixels_r; } # drop first row
115      else { pop @$pixels_r; } # drop last row
116      $$ysize_r--;
117      $fixed = 1;
118    }
119  }
120
121  # check for uni-colored rows and dup them
122  if (not $fixed) {
123    my $firstPixel = substr($$pixels_r[0], 0, $charsPerPixel);
124    my $lastPixel  = substr($$pixels_r[scalar(@$pixels_r)-1], 0, $charsPerPixel);
125
126    my $firstUniRow = createUniRow($firstPixel,$xsize);
127    my $lastUniRow  = createUniRow($lastPixel,$xsize);
128
129    my ($uniStart,$uniEnd,$ignored);
130
131    ($uniStart,$ignored) = countUniRows($firstUniRow,@$pixels_r);
132    ($ignored,$uniEnd)   = countUniRows($lastUniRow,@$pixels_r);
133
134    if ($uniStart or $uniEnd) {
135      if ($uniStart>$uniEnd) { push @$pixels_r, $lastUniRow; } # dup last row
136      else { unshift @$pixels_r, $firstUniRow; } # dup first row
137      $$ysize_r++;
138      $fixed = 1;
139    }
140  }
141
142  # try to add a transparent row
143  if (not $fixed and defined $transparent) {
144    my $emptyrow = createUniRow($transparent,$xsize);
145    push @$pixels_r, $emptyrow;
146    $$ysize_r++;
147    $fixed = 1;
148  }
149
150  # add a row in the most used color
151  # (always works, but result may be ugly)
152  if (not $fixed) {
153    my $mostUsedPixel = findMostUsedPixel(@$pixels_r,$charsPerPixel);
154    my $colorrow      = createUniRow($mostUsedPixel,$xsize);
155    push @$pixels_r, $colorrow;
156    $$ysize_r++;
157    $fixed = 1;
158  }
159
160  $fixed || die "could not fix odd size";
161}
162
163sub fixContent {
164  my @content = @_;
165  my $header = $content[0];
166  if (not $header =~ /^([0-9]+)\s+([0-9]+)\s+([0-9]+)\s+([0-9]+)\s*$/o) {
167    die "can't parse header '$header'";
168  }
169  my ($xsize,$ysize,$colors,$charsPerPixel) = ($1,$2,$3,$4);
170
171  my $elems = scalar(@content);
172  my $expected = 1+$colors+$ysize;
173  if ($elems != $expected) {
174    die "expected $expected array entries (=1+$colors+$ysize), found $elems";
175  }
176
177  my @colors = splice(@content,1,$colors);
178  my @pixels = splice(@content,1,$expected);
179
180  scalar(@content)==1 || die;
181
182  my $oddX = ($xsize % 2);
183  my $oddY = ($ysize % 2);
184
185  # for testing purposes uncomment the next lines (makes ARB-motif crash on 10.04 and b4)
186  # $oddX = 0; # accept odd xsize
187  # $oddY = 0; # accept odd ysize
188
189  if ($oddX or $oddY) {
190    my $transparent = findTransparentColor(@colors,$charsPerPixel);
191    if ($oddY) {
192      fixOddSize($ysize,@pixels,$charsPerPixel,$transparent);
193    }
194    if ($oddX) {
195      transpose(@pixels,$charsPerPixel);
196      fixOddSize($xsize,@pixels,$charsPerPixel,$transparent);
197      transpose(@pixels,$charsPerPixel);
198    }
199  }
200
201  return (
202          "$xsize $ysize $colors $charsPerPixel ",
203          @colors,
204          @pixels
205         );
206}
207
208sub parseArray($) {
209  my ($content) = @_;
210
211  my @array = ();
212  while ($content =~ /^\s*\"([^\"]*)\"[\s,]*/o) {
213    push @array, $1;
214    $content = $';
215  }
216  return @array;
217}
218
219sub parentDir($) {
220  my ($dirOrFile) = @_;
221  if ($dirOrFile =~ /\/[^\/]+$/o) { return $`; }
222  return undef;
223}
224
225sub ensureDir($);
226sub ensureDir($) {
227  my ($dir) = @_;
228  if (defined $dir) {
229    if (not -d $dir) {
230      my $dparent = parentDir($dir);
231      ensureDir($dparent);
232      if (not mkdir($dir)) {
233        # accept error if dir exists (race-condition: parallel call created dir after -d test above)
234        my $failReason="$!";
235        if (not -d $dir) {
236          die "Failed to create directory '$dir' (Reason: $failReason)";
237        }
238      }
239      else {
240        print "Created directory '$dir'\n";
241      }
242    }
243  }
244}
245
246sub main() {
247  my @source = <STDIN>;
248
249  my $args = scalar(@ARGV);
250  if ($args != 1) {
251    die "Usage: convert_xpm.pl output.xpm\n".
252      "Reads xpm from STDIN and writes result to 'output.xpm'\n";
253  }
254  my $outname = $ARGV[0];
255
256  if ($outname =~ /\/[^\/]+$/o) {
257    ensureDir(parentDir($outname));
258  }
259
260  # eliminate all comments (motif stumbles upon additional comments)
261  my @noComment = map {
262    chomp;
263    if (/\/\*/o) {
264      my ($prefix,$rest) = ($`,$');
265      /\*\//o || die "expected closing comment";
266      my $suffix = $';
267
268      if ($prefix eq '') { $_ = $suffix; }
269      elsif ($suffix eq '') { $_ = $prefix; }
270      else { $_ = $prefix.' '.$suffix; }
271    }
272    if ($_ eq '') { ; } else { $_; }
273  } @source;
274
275  my $oneLine = join(' ', @noComment);
276
277  if (not $oneLine =~ /^([^{]+{)(.+)(}[;\s]*)$/o) {
278    die "failed to parse .xpm\n(oneLine='$oneLine')\n ";
279  }
280  my ($prefix,$content,$suffix) = ($1,$2,$3);
281
282  my @content = fixContent(parseArray($content));
283
284  # print out modified .xpm
285  open(OUT,'>'.$outname) || die "Failed to write to '$outname' (Reason: $!)";
286  print OUT "/* XPM */\n";
287  print OUT "$prefix\n";
288
289  my $lines = scalar(@content);
290  for (my $i=0; $i<$lines; ++$i) {
291    print OUT '"'.$content[$i].'"';
292    print OUT ',' if ($i<($lines-1));
293    print OUT "\n";
294  }
295  print OUT "$suffix\n";
296  close(OUT);
297}
298main();
Note: See TracBrowser for help on using the repository browser.