Class: Origami::Filter::LZW

Inherits:
Object
  • Object
show all
Includes:
Origami::Filter
Defined in:
lib/origami/filters/lzw.rb

Overview

Class representing a filter used to encode and decode data with LZW compression algorithm.

Defined Under Namespace

Classes: DecodeParms

Constant Summary collapse

EOD =

:nodoc:

257
CLEARTABLE =

:nodoc:

256

Constants included from Origami::Filter

A85, AHx, CCF, Fl, RL

Instance Method Summary collapse

Methods included from Origami::Filter

included

Constructor Details

#initialize(parameters = {}) ⇒ LZW

Creates a new LZW Filter.

parameters

A hash of filter options (ignored).



53
54
55
# File 'lib/origami/filters/lzw.rb', line 53

def initialize(parameters = {})
    super(DecodeParms.new(parameters))
end

Instance Method Details

#decode(string) ⇒ Object

Decodes given data using LZW compression method.

stream

The data to decode.



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
# File 'lib/origami/filters/lzw.rb', line 114

def decode(string)
    result = "".b
    bstring = Utils::BitReader.new(string)
    codesize = 9
    table = clear(Hash.new)
    prevbyte = nil

    until bstring.eod? do
        byte = bstring.read(codesize)

        case table.size
        when 510 then codesize = 10
        when 1022 then codesize = 11
        when 2046 then codesize = 12
        when 4095
            if byte != CLEARTABLE
                raise InvalidLZWDataError.new(
                        "LZW table is full and no clear flag was set (codeword #{byte.to_s(2).rjust(codesize,'0')} at bit #{bstring.pos - codesize}/#{bstring.size})",
                        input_data: string,
                        decoded_data: result
                )
            end
        end

        if byte == CLEARTABLE
            codesize = 9
            clear table
            prevbyte = nil
            redo
        elsif byte == EOD
            break
        else
            if prevbyte.nil?
                raise InvalidLZWDataError.new(
                        "No entry for codeword #{byte.to_s(2).rjust(codesize,'0')}.",
                        input_data: string,
                        decoded_data: result
                ) unless table.value?(byte)

                prevbyte = byte
                result << table.key(byte)
                redo
            else
                raise InvalidLZWDataError.new(
                        "No entry for codeword #{prevbyte.to_s(2).rjust(codesize,'0')}.",
                        input_data: string,
                        decoded_data: result
                ) unless table.value?(prevbyte)

                if table.value?(byte)
                    entry = table.key(byte)
                else
                    entry = table.key(prevbyte)
                    entry += entry[0,1]
                end

                result << entry
                table[table.key(prevbyte) + entry[0,1]] = table.size
                prevbyte = byte
            end
        end
    end

    if @params.Predictor.is_a?(Integer)
        colors  = @params.Colors.is_a?(Integer) ?  @params.Colors.to_i : 1
        bpc     = @params.BitsPerComponent.is_a?(Integer) ? @params.BitsPerComponent.to_i : 8
        columns = @params.Columns.is_a?(Integer) ? @params.Columns.to_i : 1

        result = Predictor.do_post_prediction(result,
                                              predictor: @params.Predictor.to_i,
                                              colors: colors,
                                              bpc: bpc,
                                              columns: columns)
    end

    result
end

#encode(string) ⇒ Object

Encodes given data using LZW compression method.

stream

The data to encode.



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
# File 'lib/origami/filters/lzw.rb', line 61

def encode(string)
    if @params.Predictor.is_a?(Integer)
        colors  = @params.Colors.is_a?(Integer) ?  @params.Colors.to_i : 1
        bpc     = @params.BitsPerComponent.is_a?(Integer) ? @params.BitsPerComponent.to_i : 8
        columns = @params.Columns.is_a?(Integer) ? @params.Columns.to_i : 1

        string = Predictor.do_pre_prediction(string,
                                             predictor: @params.Predictor.to_i,
                                             colors: colors,
                                             bpc: bpc,
                                             columns: columns)
    end

    codesize = 9
    result = Utils::BitWriter.new
    result.write(CLEARTABLE, codesize)
    table = clear({})

    s = ''
    string.each_byte do |byte|
        char = byte.chr

        case table.size
        when 512 then codesize = 10
        when 1024 then codesize = 11
        when 2048 then codesize = 12
        when 4096
            result.write(CLEARTABLE, codesize)
            codesize = 9
            clear table
            redo
        end

        it = s + char
        if table.has_key?(it)
            s = it
        else
            result.write(table[s], codesize)
            table[it] = table.size
            s = char
        end
    end

    result.write(table[s], codesize)
    result.write(EOD, codesize)

    result.final.to_s
end