File: genpng

package info (click to toggle)
lcov 2.4-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 3,460 kB
  • sloc: perl: 27,911; sh: 7,320; xml: 6,982; python: 1,152; makefile: 595; cpp: 520; ansic: 176
file content (398 lines) | stat: -rwxr-xr-x 13,498 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#!/usr/bin/env perl
#
#   Copyright (c) International Business Machines  Corp., 2002
#
#   This program is free software;  you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or (at
#   your option) any later version.
#
#   This program is distributed in the hope that it will be useful, but
#   WITHOUT ANY WARRANTY;  without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
#   General Public License for more details.
#
#   You should have received a copy of the GNU General Public License
#   along with this program;  if not, see
#   <http://www.gnu.org/licenses/>.
#
#
# genpng
#
#   This script creates an overview PNG image of a source code file by
#   representing each source code character by a single pixel.
#
#   Note that the Perl module GD.pm is required for this script to work.
#   It may be obtained from http://www.cpan.org
#
# History:
#   2002-08-26: created by Peter Oberparleiter <Peter.Oberparleiter@de.ibm.com>
#

use strict;
use warnings;
use File::Basename;
use Getopt::Long;
use Cwd qw/abs_path/;
use FindBin;

use lib "$FindBin::RealBin/../lib";
use lcovutil qw (%tlaColor %tlaTextColor
                 $tool_name $tool_dir $lcov_version $lcov_url
                 die_handler warn_handler);

# Constants
# (now imported from lcovutil.pm)

# Prototypes
sub gen_png($$$$$@);
sub check_and_load_module($);
sub genpng_print_usage(*);
sub genpng_process_file($$$$$);

#
# Code entry point
#

# Check whether required module GD.pm is installed
if (check_and_load_module("GD")) {
    # Note: cannot use die() to print this message because inserting this
    # code into another script via do() would not fail as required!
    print(STDERR <<END_OF_TEXT)
ERROR: required module GD.pm not found on this system (see www.cpan.org).
END_OF_TEXT
        ;
    exit(2);
}

# Check whether we're called from the command line or from another script
if (!caller) {
    my $filename;
    my $tab_size = 4;
    my $width    = 80;
    my $dark     = 0;
    my $out_filename;
    my $help;
    my $version;

    $SIG{__WARN__} = \&warn_handler;
    $SIG{__DIE__}  = \&die_handler;

    # Parse command line options
    if (!GetOptions("tab-size=i"        => \$tab_size,
                    "width=i"           => \$width,
                    "output-filename=s" => \$out_filename,
                    "dark-mode"         => \$dark,
                    "help"              => \$help,
                    "version"           => \$version
    )) {
        print(STDERR "Use $tool_name --help to get usage information\n");
        exit(1);
    }

    $filename = $ARGV[0];

    # Check for help flag
    if ($help) {
        genpng_print_usage(*STDOUT);
        exit(0);
    }

    # Check for version flag
    if ($version) {
        print("$tool_name: $lcov_version\n");
        exit(0);
    }

    # Check options
    if (!$filename) {
        die("No filename specified\n");
    }

    # Check for output filename
    if (!$out_filename) {
        $out_filename = "$filename.png";
    }

    genpng_process_file($filename, $out_filename, $width, $tab_size, $dark);
    exit(0);
}

#
# genpng_print_usage(handle)
#
# Write out command line usage information to given filehandle.
#

sub genpng_print_usage(*)
{
    local *HANDLE = $_[0];

    print(HANDLE <<END_OF_USAGE)
Usage: $tool_name [OPTIONS] SOURCEFILE

Create an overview image for a given source code file of either plain text
or .gcov file format.

OPTIONS
  -h, --help                        Print this help, then exit
      --version                     Print version number, then exit
  -t, --tab-size TABSIZE            Use TABSIZE spaces in place of tab
  -w, --width WIDTH                 Set width of output image to WIDTH pixel
  -d, --dark-mode                   Use a light-on-dark color scheme
  -o, --output-filename FILENAME    Write image to FILENAME

For more information see the genpng man page.
END_OF_USAGE
        ;
}

#
# check_and_load_module(module_name)
#
# Check whether a module by the given name is installed on this system
# and make it known to the interpreter if available. Return undefined if it
# is installed, an error message otherwise.
#

sub check_and_load_module($)
{
    eval("use $_[0];");
    return $@;
}

#
# genpng_process_file(filename, out_filename, width, tab_size, dark)
#

sub genpng_process_file($$$$$)
{
    my $filename     = $_[0];
    my $out_filename = $_[1];
    my $width        = $_[2];
    my $tab_size     = $_[3];
    my $dark         = $_[4];
    local *HANDLE;
    my @source;

    open(HANDLE, "<", $filename) or
        die("cannot open $filename!\n");

    # Check for .gcov filename extension
    if ($filename =~ /^(.*).gcov$/) {
        # Assume gcov text format
        while (<HANDLE>) {
            if (/^\t\t(.*)$/) {
                # Uninstrumented line
                push(@source, ":$1");
            } elsif (/^      ######    (.*)$/) {
                # Line with zero execution count
                push(@source, "0:$1");
            } elsif (/^( *)(\d*)    (.*)$/) {
                # Line with positive execution count
                push(@source, "$2:$3");
            }
        }
    } else {
        # Plain text file
        while (<HANDLE>) { push(@source, ":$_"); }
    }
    close(HANDLE) or die("unable to close $filename: $!\n");

    my $show_tla = 1;
    gen_png($out_filename, $show_tla, $dark, $width, $tab_size, @source);
}

#
# gen_png(filename, show_tla, dark, width, tab_size, source)
#
# Write an overview PNG file to FILENAME. Source code is defined by SOURCE
# which is a list of lines <count>:<source code> per source code line.
# The output image will be made up of one pixel per character of source,
# coloring will be done according to execution counts. WIDTH defines the
# image width. TAB_SIZE specifies the number of spaces to use as replacement
# string for tabulator signs in source code text.
#
# Die on error.
#

sub gen_png($$$$$@)
{
    my $filename       = shift(@_);    # Filename for PNG file
    my $show_tla       = shift(@_);    # differential categories
    my $dark_mode      = shift(@_);    # dark-on-light, if set
    my $overview_width = shift(@_);    # Imagewidth for image
    my $tab_size       = shift(@_);    # Replacement string for tab signs
    my @source         = @_;           # Source code as passed via argument 2
    my $height;            # Height as define by source size
    my $overview;          # Source code overview image data
    my $col_plain_back;    # Color for overview background
    my $col_plain_text;    # Color for uninstrumented text
    my $col_cov_back;      # Color for background of covered lines
    my $col_cov_text;      # Color for text of covered lines
    my $col_nocov_back;    # Color for background of lines which
                           # were not covered (count == 0)
    my $col_nocov_text;    # Color for test of lines which were not
                           # covered (count == 0)
    my $col_hi_back;       # Color for background of highlighted lines
    my $col_hi_text;       # Color for text of highlighted lines

    my %col_tla_back;      # Color for background of TLA lines
    my %col_tla_text;      # Color for text of TLA lines

    my $line;              # Current line during iteration
    my $row = 0;           # Current row number during iteration
    my $column;            # Current column number during iteration
    my $color_text;        # Current text color during iteration
    my $color_back;        # Current background color during iteration
    my $last_tag;          # Tag of last processed line
    my $tag;               # Tag of current line
    my $last_count;        # Count of last processed line
    my $count;             # Count of current line
    my $source;            # Source code of current line
    my $replacement;       # Replacement string for tabulator chars
    local *PNG_HANDLE;     # Handle for output PNG file

    # Handle empty source files
    if (!@source) {
        @source = ("");
    }
    $height = scalar(@source);
    # Create image
    $overview = new GD::Image($overview_width, $height) or
        die("cannot allocate overview image!\n");

    # Define colors
    # overview->colorAllocate(red, green, blue)
    if ($dark_mode) {
        # just reverse foreground and background
        #  there is probably a better color scheme than this.
        $col_plain_text =
            $overview->colorAllocate(0xaa, 0xaa, 0xaa);    # light grey
        $col_plain_back = $overview->colorAllocate(0x00, 0x00, 0x00);
        $col_cov_text   = $overview->colorAllocate(0xaa, 0xa7, 0xef);
        $col_cov_back   = $overview->colorAllocate(0x5d, 0x5d, 0xea);
        $col_nocov_text = $overview->colorAllocate(0xff, 0x00, 0x00);
        $col_nocov_back = $overview->colorAllocate(0xaa, 0x00, 0x00);
        $col_hi_text    = $overview->colorAllocate(0x00, 0xff, 0x00);
        $col_hi_back    = $overview->colorAllocate(0x00, 0xaa, 0x00);
    } else {
        $col_plain_back = $overview->colorAllocate(0xff, 0xff, 0xff);
        $col_plain_text = $overview->colorAllocate(0xaa, 0xaa, 0xaa);
        $col_cov_back   = $overview->colorAllocate(0xaa, 0xa7, 0xef);
        $col_cov_text   = $overview->colorAllocate(0x5d, 0x5d, 0xea);
        $col_nocov_back = $overview->colorAllocate(0xff, 0x00, 0x00);
        $col_nocov_text = $overview->colorAllocate(0xaa, 0x00, 0x00);
        $col_hi_back    = $overview->colorAllocate(0x00, 0xff, 0x00);
        $col_hi_text    = $overview->colorAllocate(0x00, 0xaa, 0x00);
    }

    foreach my $tla (keys(%lcovutil::tlaColor)) {
        next if 'D' eq substr($tla, 0, 1); # skip deleted TLAs..they don't apper

        if ($show_tla) {
            my $text = $tlaTextColor{$tla};
            my $back = $tlaColor{$tla};
            $text =~ s/^#/0x/;
            $back =~ s/^#/0x/;
            $text = hex($text);
            $back = hex($back);

            $col_tla_back{$tla} =
                $overview->colorAllocate($back >> 16,
                                         ($back >> 8) & 0xFF,
                                         $back & 0xFF);
            $col_tla_text{$tla} =
                $overview->colorAllocate($text >> 16,
                                         ($text >> 8) & 0xFF,
                                         $text & 0xFF);
        } else {
            # no differential categories...use vanilla colors
            if (grep(/^$tla$/, ("GBC", "CBC", "GIC", "GNC"))) {
                $col_tla_back{$tla} = $col_cov_back;
                $col_tla_text{$tla} = $col_cov_text;
            } elsif (grep(/^$tla$/, ("ECB", "EUB"))) {
                $col_tla_back{$tla} = $col_plain_back;
                $col_tla_text{$tla} = $col_plain_text;
            } else {
                $col_tla_back{$tla} = $col_nocov_back;
                $col_tla_text{$tla} = $col_nocov_text;
            }
        }
    }
    # Visualize each line
    foreach $line (@source) {
        # Replace tabs with spaces to keep consistent with source
        # code view
        while ($line =~ /^([^\t]*)(\t)/) {
            $replacement = " " x ($tab_size - ((length($1) - 1) % $tab_size));
            $line =~ s/^([^\t]*)(\t)/$1$replacement/;
        }
        # Skip lines which do not follow the <count>:<line>
        # specification, otherwise $1 = count, $2 = source code
        if (!($line =~ /([-+<>=]?)(\d*):(.*)$/)) { next; }
        $tag    = $1;
        $count  = $2;
        $source = $3;

        # Decide which color pair to use
        my $tla = undef;

        # If this line was not instrumented but the one before was,
        # take the color of that line to widen color areas in
        # resulting image
        if (($count eq "") &&
            defined($last_count) &&
            ($last_count ne "")) {
            $tag   = $last_tag;
            $count = $last_count;
        }

        if ($tag eq "" && $count eq "") {
            # Line was not instrumented
            $color_text = $col_plain_text;
            $color_back = $col_plain_back;
        } else {
            die("unexpected PNG tag '$tag'")
                unless exists($lcovutil::pngMap{$tag});
            # index '1' if not covered (count is zero)
            $tla = $lcovutil::pngMap{$tag}[$count == 0];
        }

        if (defined($tla)) {
            $color_text = $col_tla_text{$tla};
            $color_back = $col_tla_back{$tla};
        }
        # Write one pixel for each source character
        $column = 0;
        foreach (split("", $source)) {
            # Check for width
            if ($column >= $overview_width) { last; }

            if ($_ eq " ") {
                # Space
                $overview->setPixel($column++, $row, $color_back);
            } else {
                # Text
                $overview->setPixel($column++, $row, $color_text);
            }
        }

        # Fill rest of line
        while ($column < $overview_width) {
            $overview->setPixel($column++, $row, $color_back);
        }

        $last_tag   = $1;
        $last_count = $2;

        $row++;
    }

    # Write PNG file
    open(PNG_HANDLE, ">", $filename) or
        die("cannot write png file $filename!\n");
    binmode(*PNG_HANDLE);
    print(PNG_HANDLE $overview->png());
    close(PNG_HANDLE) or die("unable to close $filename: $!\n");
}