/* * ps.c: PostScript printing functions. */ #include #include #include #include #include "puzzles.h" struct psdata { FILE *fp; bool colour; int ytop; bool clipped; float hatchthick, hatchspace; int gamewidth, gameheight; drawing *drawing; }; static void ps_printf(psdata *ps, const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(ps->fp, fmt, ap); va_end(ap); } static void ps_fill(psdata *ps, int colour) { int hatch; float r, g, b; print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b); if (hatch < 0) { if (ps->colour) ps_printf(ps, "%g %g %g setrgbcolor fill\n", r, g, b); else ps_printf(ps, "%g setgray fill\n", r); } else { /* Clip to the region. */ ps_printf(ps, "gsave clip\n"); /* Hatch the entire game printing area. */ ps_printf(ps, "newpath\n"); if (hatch == HATCH_VERT || hatch == HATCH_PLUS) ps_printf(ps, "0 %g %d {\n" " 0 moveto 0 %d rlineto\n" "} for\n", ps->hatchspace, ps->gamewidth, ps->gameheight); if (hatch == HATCH_HORIZ || hatch == HATCH_PLUS) ps_printf(ps, "0 %g %d {\n" " 0 exch moveto %d 0 rlineto\n" "} for\n", ps->hatchspace, ps->gameheight, ps->gamewidth); if (hatch == HATCH_SLASH || hatch == HATCH_X) ps_printf(ps, "%d %g %d {\n" " 0 moveto %d dup rlineto\n" "} for\n", -ps->gameheight, ps->hatchspace * ROOT2, ps->gamewidth, max(ps->gamewidth, ps->gameheight)); if (hatch == HATCH_BACKSLASH || hatch == HATCH_X) ps_printf(ps, "0 %g %d {\n" " 0 moveto %d neg dup neg rlineto\n" "} for\n", ps->hatchspace * ROOT2, ps->gamewidth+ps->gameheight, max(ps->gamewidth, ps->gameheight)); ps_printf(ps, "0 setgray %g setlinewidth stroke grestore\n", ps->hatchthick); } } static void ps_setcolour_internal(psdata *ps, int colour, const char *suffix) { int hatch; float r, g, b; print_get_colour(ps->drawing, colour, ps->colour, &hatch, &r, &g, &b); /* * Stroking in hatched colours is not permitted. */ assert(hatch < 0); if (ps->colour) ps_printf(ps, "%g %g %g setrgbcolor%s\n", r, g, b, suffix); else ps_printf(ps, "%g setgray%s\n", r, suffix); } static void ps_setcolour(psdata *ps, int colour) { ps_setcolour_internal(ps, colour, ""); } static void ps_stroke(psdata *ps, int colour) { ps_setcolour_internal(ps, colour, " stroke"); } static void ps_draw_text(void *handle, int x, int y, int fonttype, int fontsize, int align, int colour, const char *text) { psdata *ps = (psdata *)handle; y = ps->ytop - y; ps_setcolour(ps, colour); ps_printf(ps, "/%s findfont %d scalefont setfont\n", fonttype == FONT_FIXED ? "Courier-L1" : "Helvetica-L1", fontsize); if (align & ALIGN_VCENTRE) { ps_printf(ps, "newpath 0 0 moveto (X) true charpath flattenpath" " pathbbox\n" "3 -1 roll add 2 div %d exch sub %d exch moveto pop pop\n", y, x); } else { ps_printf(ps, "%d %d moveto\n", x, y); } ps_printf(ps, "("); while (*text) { if (*text == '\\' || *text == '(' || *text == ')') ps_printf(ps, "\\"); ps_printf(ps, "%c", *text); text++; } ps_printf(ps, ") "); if (align & (ALIGN_HCENTRE | ALIGN_HRIGHT)) ps_printf(ps, "dup stringwidth pop %sneg 0 rmoveto show\n", (align & ALIGN_HCENTRE) ? "2 div " : ""); else ps_printf(ps, "show\n"); } static void ps_draw_rect(void *handle, int x, int y, int w, int h, int colour) { psdata *ps = (psdata *)handle; y = ps->ytop - y; /* * Offset by half a pixel for the exactness requirement. */ ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto" " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w); ps_fill(ps, colour); } static void ps_draw_line(void *handle, int x1, int y1, int x2, int y2, int colour) { psdata *ps = (psdata *)handle; y1 = ps->ytop - y1; y2 = ps->ytop - y2; ps_printf(ps, "newpath %d %d moveto %d %d lineto\n", x1, y1, x2, y2); ps_stroke(ps, colour); } static void ps_draw_polygon(void *handle, const int *coords, int npoints, int fillcolour, int outlinecolour) { psdata *ps = (psdata *)handle; int i; ps_printf(ps, "newpath %d %d moveto\n", coords[0], ps->ytop - coords[1]); for (i = 1; i < npoints; i++) ps_printf(ps, "%d %d lineto\n", coords[i*2], ps->ytop - coords[i*2+1]); ps_printf(ps, "closepath\n"); if (fillcolour >= 0) { ps_printf(ps, "gsave\n"); ps_fill(ps, fillcolour); ps_printf(ps, "grestore\n"); } ps_stroke(ps, outlinecolour); } static void ps_draw_circle(void *handle, int cx, int cy, int radius, int fillcolour, int outlinecolour) { psdata *ps = (psdata *)handle; cy = ps->ytop - cy; ps_printf(ps, "newpath %d %d %d 0 360 arc closepath\n", cx, cy, radius); if (fillcolour >= 0) { ps_printf(ps, "gsave\n"); ps_fill(ps, fillcolour); ps_printf(ps, "grestore\n"); } ps_stroke(ps, outlinecolour); } static void ps_unclip(void *handle) { psdata *ps = (psdata *)handle; assert(ps->clipped); ps_printf(ps, "grestore\n"); ps->clipped = false; } static void ps_clip(void *handle, int x, int y, int w, int h) { psdata *ps = (psdata *)handle; if (ps->clipped) ps_unclip(ps); y = ps->ytop - y; /* * Offset by half a pixel for the exactness requirement. */ ps_printf(ps, "gsave\n"); ps_printf(ps, "newpath %g %g moveto %d 0 rlineto 0 %d rlineto" " %d 0 rlineto closepath\n", x - 0.5, y + 0.5, w, -h, -w); ps_printf(ps, "clip\n"); ps->clipped = true; } static void ps_line_width(void *handle, float width) { psdata *ps = (psdata *)handle; ps_printf(ps, "%g setlinewidth\n", width); } static void ps_line_dotted(void *handle, bool dotted) { psdata *ps = (psdata *)handle; if (dotted) { ps_printf(ps, "[ currentlinewidth 3 mul ] 0 setdash\n"); } else { ps_printf(ps, "[ ] 0 setdash\n"); } } static char *ps_text_fallback(void *handle, const char *const *strings, int nstrings) { /* * We can handle anything in ISO 8859-1, and we'll manually * translate it out of UTF-8 for the purpose. */ int i, maxlen; char *ret; maxlen = 0; for (i = 0; i < nstrings; i++) { int len = strlen(strings[i]); if (maxlen < len) maxlen = len; } ret = snewn(maxlen + 1, char); for (i = 0; i < nstrings; i++) { const char *p = strings[i]; char *q = ret; while (*p) { int c = (unsigned char)*p++; if (c < 0x80) { *q++ = c; /* ASCII */ } else if ((c == 0xC2 || c == 0xC3) && (*p & 0xC0) == 0x80) { *q++ = (c << 6) | (*p++ & 0x3F); /* top half of 8859-1 */ } else { break; } } if (!*p) { *q = '\0'; return ret; } } assert(!"Should never reach here"); return NULL; } static void ps_begin_doc(void *handle, int pages) { psdata *ps = (psdata *)handle; fputs("%!PS-Adobe-3.0\n", ps->fp); fputs("%%Creator: Simon Tatham's Portable Puzzle Collection\n", ps->fp); fputs("%%DocumentData: Clean7Bit\n", ps->fp); fputs("%%LanguageLevel: 1\n", ps->fp); fprintf(ps->fp, "%%%%Pages: %d\n", pages); fputs("%%DocumentNeededResources:\n", ps->fp); fputs("%%+ font Helvetica\n", ps->fp); fputs("%%+ font Courier\n", ps->fp); fputs("%%EndComments\n", ps->fp); fputs("%%BeginSetup\n", ps->fp); fputs("%%IncludeResource: font Helvetica\n", ps->fp); fputs("%%IncludeResource: font Courier\n", ps->fp); fputs("%%EndSetup\n", ps->fp); fputs("%%BeginProlog\n", ps->fp); /* * Re-encode Helvetica and Courier into ISO-8859-1, which gives * us times and divide signs - and also (according to the * Language Reference Manual) a bonus in that the ASCII '-' code * point now points to a minus sign instead of a hyphen. */ fputs("/Helvetica findfont " /* get the font dictionary */ "dup maxlength dict dup begin " /* create and open a new dict */ "exch " /* move the original font to top of stack */ "{1 index /FID ne {def} {pop pop} ifelse} forall " /* copy everything except FID */ "/Encoding ISOLatin1Encoding def " /* set the thing we actually wanted to change */ "/FontName /Helvetica-L1 def " /* set a new font name */ "FontName end exch definefont" /* and define the font */ "\n", ps->fp); fputs("/Courier findfont " /* get the font dictionary */ "dup maxlength dict dup begin " /* create and open a new dict */ "exch " /* move the original font to top of stack */ "{1 index /FID ne {def} {pop pop} ifelse} forall " /* copy everything except FID */ "/Encoding ISOLatin1Encoding def " /* set the thing we actually wanted to change */ "/FontName /Courier-L1 def " /* set a new font name */ "FontName end exch definefont" /* and define the font */ "\n", ps->fp); fputs("%%EndProlog\n", ps->fp); } static void ps_begin_page(void *handle, int number) { psdata *ps = (psdata *)handle; fprintf(ps->fp, "%%%%Page: %d %d\ngsave save\n%g dup scale\n", number, number, 72.0 / 25.4); } static void ps_begin_puzzle(void *handle, float xm, float xc, float ym, float yc, int pw, int ph, float wmm) { psdata *ps = (psdata *)handle; fprintf(ps->fp, "gsave\n" "clippath flattenpath pathbbox pop pop translate\n" "clippath flattenpath pathbbox 4 2 roll pop pop\n" "exch %g mul %g add exch dup %g mul %g add sub translate\n" "%g dup scale\n" "0 -%d translate\n", xm, xc, ym, yc, wmm/pw, ph); ps->ytop = ph; ps->clipped = false; ps->gamewidth = pw; ps->gameheight = ph; ps->hatchthick = 0.2 * pw / wmm; ps->hatchspace = 1.0 * pw / wmm; } static void ps_end_puzzle(void *handle) { psdata *ps = (psdata *)handle; fputs("grestore\n", ps->fp); } static void ps_end_page(void *handle, int number) { psdata *ps = (psdata *)handle; fputs("restore grestore showpage\n", ps->fp); } static void ps_end_doc(void *handle) { psdata *ps = (psdata *)handle; fputs("%%EOF\n", ps->fp); } static const struct drawing_api ps_drawing = { ps_draw_text, ps_draw_rect, ps_draw_line, ps_draw_polygon, ps_draw_circle, NULL /* draw_update */, ps_clip, ps_unclip, NULL /* start_draw */, NULL /* end_draw */, NULL /* status_bar */, NULL /* blitter_new */, NULL /* blitter_free */, NULL /* blitter_save */, NULL /* blitter_load */, ps_begin_doc, ps_begin_page, ps_begin_puzzle, ps_end_puzzle, ps_end_page, ps_end_doc, ps_line_width, ps_line_dotted, ps_text_fallback, }; psdata *ps_init(FILE *outfile, bool colour) { psdata *ps = snew(psdata); ps->fp = outfile; ps->colour = colour; ps->ytop = 0; ps->clipped = false; ps->hatchthick = ps->hatchspace = ps->gamewidth = ps->gameheight = 0; ps->drawing = drawing_new(&ps_drawing, NULL, ps); return ps; } void ps_free(psdata *ps) { drawing_free(ps->drawing); sfree(ps); } drawing *ps_drawing_api(psdata *ps) { return ps->drawing; } ' href='#n254'>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
#!/usr/bin/ruby
# (c) 2010 by Thomas Martitz
#
# 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.

#
# parse test codec output files and give wiki or spreadsheet formatted output
#
class CodecResult
    include Comparable
private

    attr_writer :codec
    attr_writer :decoded_frames
    attr_writer :max_frames
    attr_writer :decode_time
    attr_writer :file_duration
    attr_writer :percent_realtime
    attr_writer :mhz_needed

    def get_codec(filename)
        case filename
            when /nero_hev2_.+/
                self.codec = "Nero AAC-HEv2 (SBR+PS)"
            when /.+aache.+/, /nero_he_.+/
                self.codec = "Nero AAC-HE (SBR)"
            when /a52.+/
                self.codec = "AC3 (A52)"
            when /ape_.+/
                self.codec = "Monkey Audio"
            when /lame_.+/
                self.codec = "MP3"
            when /.+\.m4a/
                self.codec = "AAC-LC"
            when /vorbis.+/
                self.codec = "Vorbis"
            when /wma_.+/
                self.codec = "WMA Standard"
            when /wv_.+/
                self.codec = "WAVPACK"
            when /applelossless.+/
                self.codec = "Apple Lossless"
            when /mpc_.+/
                self.codec = "Musepack"
            when /flac_.+/
                self.codec = "FLAC"
            when /cook_.+/
                self.codec = "Cook (RA)"
            when /atrac3.+/
                self.codec = "Atrac3"
            when /true.+/
                self.codec = "True Audio"
            when /toolame.+/, /pegase_l2.+/
                self.codec = "MP2"
            when /atrack1.+/
                self.codec = "Atrac1"
            when /wmapro.+/
                self.codec = "WMA Professional"
            when /wmal.+/
                self.codec = "WMA Lossless"
            when /speex.+/
                self.codec = "Speex"
            when /pegase_l1.+/
                self.codec = "MP1"
            else
                self.codec = "CODEC UNKNOWN (#{name})"
        end
    end

    def file_name=(name)
        @file_name = name
        get_codec(name)
    end

public

    attr_reader :file_name
    attr_reader :codec
    attr_reader :decoded_frames
    attr_reader :max_frames
    attr_reader :decode_time
    attr_reader :file_duration
    attr_reader :percent_realtime
    attr_reader :mhz_needed

    # make results comparable, allows for simple faster/slower/equal
    def <=>(other)
        if self.file_name != other.file_name
            raise ArgumentError, "Cannot compare different files"
        end
        return self.decode_time <=> other.decode_time
    end

    def initialize(text_block, cpu_freq = nil)
        # we need an Array
        c = text_block.class
        if (c != Array && c.superclass != Array)
            raise ArgumentError,
                 "Argument must be an array but is " + text_block.class.to_s
        end

        #~ lame_192.mp3
        #~ 175909 of 175960
        #~ Decode time - 8.84s
        #~ File duration - 175.96s
        #~ 1990.49% realtime
        #~ 30.14MHz needed for realtime (not there in RaaA)

        # file name
        self.file_name = text_block[0]

        # decoded & max frames
        test = Regexp.new(/(\d+) of (\d+)/)
        res = text_block[1].match(test)
        self.decoded_frames = res[1].to_i
        self.max_frames = res[2].to_i

        # decode time, in centiseconds
        test = Regexp.new(/Decode time - ([.\d]+)s/)
        self.decode_time = text_block[2].match(test)[1].to_f

        # file duration, in centiseconds
        test = Regexp.new(/File duration - ([.\d]+)s/)
        self.file_duration = text_block[3].match(test)[1].to_f

        # % realtime
        self.percent_realtime = text_block[4].to_f

        # MHz needed for rt
        test = Regexp.new(/[.\d]+MHz needed for realtime/)
        self.mhz_needed = nil
        if (text_block[5] != nil && text_block[5].length > 0)
            self.mhz_needed = text_block[5].match(test)[0].to_f
        elsif (cpu_freq)
            # if not given, calculate it as per passed cpu frequency
            # duration to microseconds
            speed = self.file_duration / self.decode_time
            self.mhz_needed = cpu_freq / speed
        end
    end
end

class TestCodecResults < Array
    def initialize(file_name, cpu_freq)
        super()
        temp = self.clone
        # go through the results, create a CodecResult for each block
        # of text (results for the codecs are seperated by an empty line)
        File.open(file_name, File::RDONLY) do |file|
            file.each_chomp do |line|
                if (line.length == 0) then
                    self << CodecResult.new(temp, cpu_freq);temp.clear
                else
                    temp << line
                end
            end
        end
    end

    # sort the results by filename (so files of the same codec are near)
    def sort
        super { |x, y| x.file_name <=> y.file_name }
    end
end

class File
    # walk through each line but have the \n removed
    def each_chomp
        self.each_line do |line|
            yield(line.chomp)
        end
    end
end

class Float
    alias_method(:old_to_s, :to_s)
    # add the ability to use a different decimal seperator in to_s
    def to_s
        string = old_to_s
        string.sub!(/[.]/ , @@dec_sep) if @@dec_sep
        string
    end

    @@dec_sep = nil
    def self.decimal_seperator=(sep)
        @@dec_sep=sep
    end
end

#files is an Array of TestCodecResultss
def for_calc(files)
    files[0].each_index do |i|
        string = files[0][i].file_name + "\t"
        for f in files
            string += f[i].percent_realtime.to_s + "%\t"
        end
        puts string
    end
end

#files is an Array of TestCodecResultss
def for_wiki(files)
    basefile = files.shift
    codec = nil
    basefile.each_index do |i| res = basefile[i]
        # make a joined row for each codec
        if (codec == nil || res.codec != codec) then
            codec = res.codec
            puts "| *%s*  ||||%s" % [codec, "|"*files.length]
        end
        row = sprintf("| %s | %.2f%%%% realtime | Decode time - %.2fs |" %
                        [res.file_name, res.percent_realtime, res.decode_time])
        if (res.mhz_needed != nil) # column for mhz needed, | - | if unknown
            row += sprintf(" %.2fMHz |" % res.mhz_needed.to_s)
        else
            row += " - |"
        end
        for f in files  # calculate speed up compared to the rest files
            delta = (res.percent_realtime / f[i].percent_realtime)*100
            row += sprintf(" %.2f%%%% |" % delta)
        end
        puts row
    end
end

# for_xml() anyone? :)

def help
    puts "#{$0} [OPTIONS] FILE [FILES]..."
    puts "Options:\t-w\tOutput in Fosswiki format (default)"
    puts "\t\t-c\tOutput in Spreadsheet-compatible format (tab-seperated)"
    puts "\t\t-s=MHZ\tAssume MHZ cpu frequency for \"MHz needed for realtime\" calculation"
    puts "\t\t\t(if not given by the log files, e.g. for RaaA)"
    puts "\t\t-d=CHAR\tUse CHAR as decimal seperator in the -c output"
    puts "\t\t\t(if your spreadsheed tool localized and making problems)"
    puts
    puts "\tOne file is needed. This is the basefile."
    puts "\tIn -c output, the % realtime values of each"
    puts "\tcodec from each file is printed on the screen onto the screen"
    puts "\tIn -w output, a wiki table is made from the basefile with one column"
    puts "\tfor each additional file representing relative speed of the basefile"
    exit
end

to_call = method(:for_wiki)
mhz = nil
files = []

help if (ARGV.length == 0)

ARGV.each do |e|
    a = e.chars.to_a
    if (a[0] == '-') # option
        case a[1]
            when 'c'
                to_call = method(:for_calc)
            when 'w'
                to_call = method(:for_wiki)
            when 'd'
                if (a[2] == '=')
                    sep = a[3]
                else
                    sep = a[2]
                end
                Float.decimal_seperator = sep
            when 's'
                if (a[2] == '=')
                    mhz = a[3..-1].join.to_i
                else
                    mhz = a[2..-1].join.to_i
                end
            else
                help
        end
    else # filename
        files << e
    end
end


tmp = []
for file in files do
    tmp << TestCodecResults.new(file, mhz).sort
end
to_call.call(tmp) # invoke selected method