img_to_script 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.rubocop.yml +25 -0
- data/.vscode/launch.json +21 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +271 -0
- data/Rakefile +16 -0
- data/img_to_script.gemspec +41 -0
- data/lib/img_to_script/abs_token_type.rb +33 -0
- data/lib/img_to_script/abstract_token/abs_func.rb +19 -0
- data/lib/img_to_script/abstract_token/abstract_token.rb +23 -0
- data/lib/img_to_script/abstract_token/assign_value.rb +20 -0
- data/lib/img_to_script/abstract_token/clear_screen.rb +16 -0
- data/lib/img_to_script/abstract_token/data_read.rb +19 -0
- data/lib/img_to_script/abstract_token/data_store.rb +19 -0
- data/lib/img_to_script/abstract_token/draw_chunk_by_hex_value.rb +19 -0
- data/lib/img_to_script/abstract_token/draw_line_by_abs_coords.rb +22 -0
- data/lib/img_to_script/abstract_token/draw_pixel_by_abs_coords.rb +20 -0
- data/lib/img_to_script/abstract_token/go_to.rb +19 -0
- data/lib/img_to_script/abstract_token/if_condition.rb +20 -0
- data/lib/img_to_script/abstract_token/loop_begin.rb +21 -0
- data/lib/img_to_script/abstract_token/loop_end.rb +19 -0
- data/lib/img_to_script/abstract_token/math_add.rb +20 -0
- data/lib/img_to_script/abstract_token/math_greater_than.rb +20 -0
- data/lib/img_to_script/abstract_token/math_mult.rb +20 -0
- data/lib/img_to_script/abstract_token/math_sub.rb +20 -0
- data/lib/img_to_script/abstract_token/move_point_to_abs_coords.rb +20 -0
- data/lib/img_to_script/abstract_token/parentheses.rb +19 -0
- data/lib/img_to_script/abstract_token/program_begin.rb +16 -0
- data/lib/img_to_script/abstract_token/program_end.rb +16 -0
- data/lib/img_to_script/abstract_token/remark.rb +19 -0
- data/lib/img_to_script/abstract_token/sign_func.rb +19 -0
- data/lib/img_to_script/abstract_token/wait.rb +19 -0
- data/lib/img_to_script/abstract_token.rb +8 -0
- data/lib/img_to_script/container.rb +26 -0
- data/lib/img_to_script/current_line_placeholder.rb +40 -0
- data/lib/img_to_script/formatter.rb +34 -0
- data/lib/img_to_script/generators/generator.rb +134 -0
- data/lib/img_to_script/generators/hex_mask/default.rb +24 -0
- data/lib/img_to_script/generators/hex_mask/enhanced.rb +220 -0
- data/lib/img_to_script/generators/hex_mask/hex_mask.rb +100 -0
- data/lib/img_to_script/generators/hex_mask.rb +13 -0
- data/lib/img_to_script/generators/run_length_encoding/horizontal.rb +78 -0
- data/lib/img_to_script/generators/run_length_encoding/run_length_encoding.rb +304 -0
- data/lib/img_to_script/generators/run_length_encoding/vertical.rb +79 -0
- data/lib/img_to_script/generators/run_length_encoding.rb +10 -0
- data/lib/img_to_script/generators/segmental/data_read_draw/data_read_draw.rb +70 -0
- data/lib/img_to_script/generators/segmental/data_read_draw/horizontal.rb +54 -0
- data/lib/img_to_script/generators/segmental/data_read_draw/vertical.rb +54 -0
- data/lib/img_to_script/generators/segmental/data_read_draw.rb +18 -0
- data/lib/img_to_script/generators/segmental/direct_draw/direct_draw.rb +62 -0
- data/lib/img_to_script/generators/segmental/direct_draw/horizontal.rb +16 -0
- data/lib/img_to_script/generators/segmental/direct_draw/vertical.rb +16 -0
- data/lib/img_to_script/generators/segmental/direct_draw.rb +12 -0
- data/lib/img_to_script/generators/segmental/horizontal_mixin.rb +32 -0
- data/lib/img_to_script/generators/segmental/segmental.rb +101 -0
- data/lib/img_to_script/generators/segmental/vertical_mixin.rb +38 -0
- data/lib/img_to_script/generators/segmental.rb +10 -0
- data/lib/img_to_script/generators.rb +27 -0
- data/lib/img_to_script/import.rb +5 -0
- data/lib/img_to_script/language_token.rb +8 -0
- data/lib/img_to_script/languages/mk90_basic/formatters/formatter.rb +49 -0
- data/lib/img_to_script/languages/mk90_basic/formatters/minificator.rb +316 -0
- data/lib/img_to_script/languages/mk90_basic/formatters/sliceable_tokens_mixin.rb +15 -0
- data/lib/img_to_script/languages/mk90_basic/formatters.rb +13 -0
- data/lib/img_to_script/languages/mk90_basic/mk90_basic_token.rb +59 -0
- data/lib/img_to_script/languages/mk90_basic/translators/mixin.rb +205 -0
- data/lib/img_to_script/languages/mk90_basic/translators/mk90_basic_10.rb +27 -0
- data/lib/img_to_script/languages/mk90_basic/translators/mk90_basic_20.rb +27 -0
- data/lib/img_to_script/languages/mk90_basic/translators/translator.rb +205 -0
- data/lib/img_to_script/languages/mk90_basic/translators.rb +13 -0
- data/lib/img_to_script/languages/mk90_basic.rb +17 -0
- data/lib/img_to_script/languages.rb +10 -0
- data/lib/img_to_script/task.rb +26 -0
- data/lib/img_to_script/translator.rb +31 -0
- data/lib/img_to_script/version.rb +5 -0
- data/lib/img_to_script.rb +19 -0
- data/sig/img_to_script.rbs +4 -0
- metadata +204 -0
@@ -0,0 +1,220 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImgToScript
|
4
|
+
module Generators
|
5
|
+
module HexMask
|
6
|
+
#
|
7
|
+
# Generates image rendering script using the DRAW/M statement with
|
8
|
+
# the 'enhancements'.
|
9
|
+
#
|
10
|
+
# To save space at the MPO-10 cart, it's possible to replacelong
|
11
|
+
# sequences of repeating chunks with a shorter
|
12
|
+
# (in terms of number of chars required to store the statement) code,
|
13
|
+
# e.g.:
|
14
|
+
#
|
15
|
+
# xxDRAWMFFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFF
|
16
|
+
# // 43 bytes -- 16 x 'AA'.
|
17
|
+
# xxDRAWMFF:FORI=1TO16:DRAWMAA:NEXTI:DRAWMFF
|
18
|
+
# // 42 bytes -- repeating chunks were replaced with a loop.
|
19
|
+
#
|
20
|
+
# Same principle could be applied to eliminate long sequences of white
|
21
|
+
# pixels, e.g.:
|
22
|
+
# xxDRAWMFF00000000000000000000FF
|
23
|
+
# // 31 bytes -- 10 x '00'.
|
24
|
+
# xxDRAWMFF:DRAWOxxx,yy:DRAWMFF
|
25
|
+
# // 29 bytes -- replaced with a manual shift.
|
26
|
+
#
|
27
|
+
# The algorithm is:
|
28
|
+
# 1. apply RLE to the array of hex chunks (DRAW/M arguments);
|
29
|
+
# 2. loop through the array of run-lengths: take an element (a 'chunk') and
|
30
|
+
# heck if it's run-length is larger than an estimated value (MIN_REP_CHUNKS
|
31
|
+
# or MIN_REP_WH_CHUNKS);
|
32
|
+
# 3. if true: add pending chunks to the BASIC code; then append a FOR-NEXT loop or
|
33
|
+
# DRAW/O. Clear pending chunks array and continue to loop through the array of
|
34
|
+
# run-lengths.
|
35
|
+
# 4. otherwise: add current chunk to the array of pending chunks;
|
36
|
+
# 5. after the loop: add pending chunks.
|
37
|
+
#
|
38
|
+
class Enhanced < HexMask
|
39
|
+
MIN_REP_CHUNKS = 16
|
40
|
+
MIN_REP_WH_CHUNKS = 9
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def _generate
|
45
|
+
_init_instant_vars
|
46
|
+
_process_rle_image
|
47
|
+
_append_pending_chunks
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Set up initional values for the instant variables.
|
52
|
+
#
|
53
|
+
def _init_instant_vars
|
54
|
+
@x = 0 # It is always = 0, not the x/y_offset. The reason is that
|
55
|
+
@y = 0 # we've already applied offset in the #_prepare_image method.
|
56
|
+
|
57
|
+
@hex_img = _encode_img
|
58
|
+
@rle_hex_img = _encode_hex_img
|
59
|
+
|
60
|
+
@pending_chunks = []
|
61
|
+
@run_length_index = 0
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Loop through the RLE-encoded hex-image.
|
66
|
+
#
|
67
|
+
def _process_rle_image
|
68
|
+
@rle_hex_img.each_with_index do |rle_item, idx|
|
69
|
+
@chunk_count = rle_item.run_length
|
70
|
+
@chunk = rle_item.chunk
|
71
|
+
|
72
|
+
_calc_current_pos
|
73
|
+
_process_chunk(idx)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
#
|
78
|
+
# Choose action for a current chunk depending on its run-length
|
79
|
+
# and its color hex value (white or non-white).
|
80
|
+
#
|
81
|
+
# @param [Integer] idx
|
82
|
+
# Index of the element in the RLE array.
|
83
|
+
#
|
84
|
+
def _process_chunk(idx)
|
85
|
+
if @chunk_count >= MIN_REP_WH_CHUNKS && @chunk == "00"
|
86
|
+
_process_white_segments(idx)
|
87
|
+
elsif @chunk_count >= MIN_REP_CHUNKS && @chunk != "00"
|
88
|
+
_process_non_white_segments
|
89
|
+
else
|
90
|
+
_upd_pending_chunks
|
91
|
+
_upd_rle_index
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
#
|
96
|
+
# Run-length encoding of the @hex_img.
|
97
|
+
#
|
98
|
+
# @return [Array<RunLengthEncodingRb::RLEElement>]
|
99
|
+
#
|
100
|
+
def _encode_hex_img
|
101
|
+
RunLengthEncodingRb.encode(@hex_img)
|
102
|
+
end
|
103
|
+
|
104
|
+
#
|
105
|
+
# Calculate current (X, Y) position.
|
106
|
+
#
|
107
|
+
def _calc_current_pos
|
108
|
+
@y += @chunk_count
|
109
|
+
return unless @y > @scr_height - 1
|
110
|
+
|
111
|
+
# Reached the vertical end of the screen - re-calculate position:
|
112
|
+
div_res = @y.div(@scr_height)
|
113
|
+
@x += CHUNK_WIDTH * div_res
|
114
|
+
@y = @y.remainder(@scr_height)
|
115
|
+
end
|
116
|
+
|
117
|
+
def _upd_rle_index
|
118
|
+
@run_length_index += @chunk_count
|
119
|
+
end
|
120
|
+
|
121
|
+
def _clr_pending_chunks
|
122
|
+
@pending_chunks = []
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Replace long sequences of repeating non-white chunks with a shorter FOR-NEXT loop.
|
127
|
+
#
|
128
|
+
def _process_non_white_segments
|
129
|
+
_append_pending_chunks
|
130
|
+
_clr_pending_chunks
|
131
|
+
|
132
|
+
_append_start_loop(@chunk_count)
|
133
|
+
_append_hex_values(Array(@chunk))
|
134
|
+
_append_end_loop
|
135
|
+
|
136
|
+
_upd_rle_index
|
137
|
+
end
|
138
|
+
|
139
|
+
def _append_hex_values(hex_values)
|
140
|
+
@tokens.append(
|
141
|
+
AbstractToken::DrawChunkByHexValue.new(
|
142
|
+
hex_values: hex_values
|
143
|
+
)
|
144
|
+
)
|
145
|
+
end
|
146
|
+
|
147
|
+
def _append_start_loop(end_value)
|
148
|
+
@tokens.append(
|
149
|
+
AbstractToken::LoopBegin.new(
|
150
|
+
var_name: LOOP_VAR,
|
151
|
+
start_value: 1,
|
152
|
+
end_value: end_value
|
153
|
+
)
|
154
|
+
)
|
155
|
+
end
|
156
|
+
|
157
|
+
def _append_end_loop
|
158
|
+
@tokens.append(
|
159
|
+
AbstractToken::LoopEnd.new(
|
160
|
+
var_name: LOOP_VAR
|
161
|
+
)
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
def _append_move_point(x, y)
|
166
|
+
@tokens.append(
|
167
|
+
AbstractToken::MovePointToAbsCoords.new(
|
168
|
+
x: x,
|
169
|
+
y: y
|
170
|
+
)
|
171
|
+
)
|
172
|
+
end
|
173
|
+
|
174
|
+
#
|
175
|
+
# Replace long sequences of white chunks (0x00) with a shorter manual shift of the (X, Y) point.
|
176
|
+
#
|
177
|
+
# @param [Integer] idx
|
178
|
+
# Index of the element in the RLE array.
|
179
|
+
#
|
180
|
+
def _process_white_segments(idx)
|
181
|
+
# If white segments are the last part of the image,
|
182
|
+
# we don't need to store them in the BASIC script at all
|
183
|
+
return if idx == @rle_hex_img.length - 1
|
184
|
+
|
185
|
+
_append_pending_chunks
|
186
|
+
_clr_pending_chunks
|
187
|
+
|
188
|
+
_append_move_point(@x, @y)
|
189
|
+
|
190
|
+
_upd_rle_index
|
191
|
+
end
|
192
|
+
|
193
|
+
#
|
194
|
+
# Append accumulated pending chunks to the tokens array.
|
195
|
+
#
|
196
|
+
def _append_pending_chunks
|
197
|
+
return if @pending_chunks == []
|
198
|
+
|
199
|
+
_append_hex_values(@pending_chunks)
|
200
|
+
end
|
201
|
+
|
202
|
+
#
|
203
|
+
# Update array of the pending chunks.
|
204
|
+
#
|
205
|
+
def _upd_pending_chunks
|
206
|
+
@pending_chunks.concat _current_pending_chunks
|
207
|
+
end
|
208
|
+
|
209
|
+
#
|
210
|
+
# Retun chunks that are pending at the current itteration.
|
211
|
+
#
|
212
|
+
# @return [Array<String>]
|
213
|
+
#
|
214
|
+
def _current_pending_chunks
|
215
|
+
@hex_img.slice(@run_length_index, @chunk_count)
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImgToScript
|
4
|
+
module Generators
|
5
|
+
module HexMask
|
6
|
+
#
|
7
|
+
# Base class for the hex-mask-based generators.
|
8
|
+
#
|
9
|
+
class HexMask < Generator
|
10
|
+
CHUNK_WIDTH = 8
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
#
|
15
|
+
# Encode a binary image to a hex-mask encoded image.
|
16
|
+
#
|
17
|
+
# @return [Array<String>] hex_img
|
18
|
+
# An encoded image, represented as an array of hex-chunks.
|
19
|
+
#
|
20
|
+
def _encode_img
|
21
|
+
image = _prepare_image
|
22
|
+
|
23
|
+
hex_img = []
|
24
|
+
|
25
|
+
(0...image.width).step(CHUNK_WIDTH).each do |x|
|
26
|
+
(0...image.height).each do |y|
|
27
|
+
chunk = _grab_chunk(image, x, y)
|
28
|
+
hex_img.push(_bin_to_hex(chunk))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
hex_img
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Prepare the image:
|
37
|
+
# 1. ensure that image width is divisible by 8 (CHUNK_WIDTH);
|
38
|
+
# 2. ensure that image height is equal to @scr_height;
|
39
|
+
# 3. offset the image according to the provided @x_offset / @y_offset.
|
40
|
+
#
|
41
|
+
# Steps 1 & 2 are required by the DRAW/M statement's algorithm.
|
42
|
+
#
|
43
|
+
def _prepare_image
|
44
|
+
@image.extent(_new_width,
|
45
|
+
@scr_height,
|
46
|
+
@x_offset * -1,
|
47
|
+
@y_offset * -1)
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Calculate new width for the image.
|
52
|
+
#
|
53
|
+
# The algorithm of the DRAW/M statement requires that image width should
|
54
|
+
# be divisible by 8 (CHUNK_WIDTH). If the image doesn't comply with this
|
55
|
+
# condition, it is required to extend the image width to a nearlest number
|
56
|
+
# n: n.remainder(CHUNK_WIDTH) == 0.
|
57
|
+
#
|
58
|
+
# @return [Integer]
|
59
|
+
# New width.
|
60
|
+
#
|
61
|
+
def _new_width
|
62
|
+
width = ((@image.width + @x_offset) / CHUNK_WIDTH.to_f).ceil * CHUNK_WIDTH
|
63
|
+
width.clamp(CHUNK_WIDTH, @scr_width)
|
64
|
+
end
|
65
|
+
|
66
|
+
#
|
67
|
+
# Grab a 8 x 1 rectangle from the image and convert its pixels to a binary string representation.
|
68
|
+
#
|
69
|
+
# @param [Magick::Image, Magick::BinMagick::Image] image
|
70
|
+
#
|
71
|
+
# @param [Integer] x, y
|
72
|
+
# A current X, Y position at the image.
|
73
|
+
#
|
74
|
+
# @return [String]
|
75
|
+
# An 8-character string of 0's and 1's. Example: "11110011"
|
76
|
+
#
|
77
|
+
def _grab_chunk(image, x, y)
|
78
|
+
chunk_width = CHUNK_WIDTH
|
79
|
+
chunk_heigth = 1
|
80
|
+
pixels = image.get_pixels(x, y, chunk_width, chunk_heigth)
|
81
|
+
|
82
|
+
pixels.map { |pixel| pixel.to_color == "black" ? 1 : 0 }.join("")
|
83
|
+
end
|
84
|
+
|
85
|
+
#
|
86
|
+
# Convert a string of binary values to a two-digit hex representation.
|
87
|
+
#
|
88
|
+
# Example: "11110011" => "F3"
|
89
|
+
#
|
90
|
+
# @param [String] str
|
91
|
+
#
|
92
|
+
# @return [String]
|
93
|
+
#
|
94
|
+
def _bin_to_hex(str)
|
95
|
+
format("%02x", str.to_i(2)).upcase
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImgToScript
|
4
|
+
module Generators
|
5
|
+
#
|
6
|
+
# Each 8x1 pixels block of a binary image gets encoded as a hex value.
|
7
|
+
#
|
8
|
+
# The method natively supported only by the Elektronika MK90 BASIC with its
|
9
|
+
# DRAWM statement.
|
10
|
+
#
|
11
|
+
module HexMask; end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImgToScript
|
4
|
+
module Generators
|
5
|
+
module RunLengthEncoding
|
6
|
+
#
|
7
|
+
# RLE - horizontal scanlines.
|
8
|
+
#
|
9
|
+
class Horizontal < RunLengthEncoding
|
10
|
+
private
|
11
|
+
|
12
|
+
def _generate
|
13
|
+
@pixels = @image.get_pixels(0, 0, @image.width, @image.height)
|
14
|
+
_encode_pixels
|
15
|
+
_append_decoder
|
16
|
+
end
|
17
|
+
|
18
|
+
def _append_decoder
|
19
|
+
@image_size = @image.width + @x_offset
|
20
|
+
@segment_size = @image_size - 1
|
21
|
+
|
22
|
+
@major_axis_symbol = X_LBL
|
23
|
+
@minor_axis_symbol = Y_LBL
|
24
|
+
|
25
|
+
@major_axis_value = @x_offset
|
26
|
+
|
27
|
+
@part_line_pattern = _part_line_pattern
|
28
|
+
@full_line_pattern = _full_line_pattern
|
29
|
+
|
30
|
+
super
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Part-length line, i.e. a line that fills only
|
35
|
+
# a part of the scan line.
|
36
|
+
#
|
37
|
+
# Formatted for the horizontal scan lines.
|
38
|
+
#
|
39
|
+
def _part_line_pattern
|
40
|
+
AbstractToken::DrawLineByAbsCoords.new(
|
41
|
+
x0: X_LBL,
|
42
|
+
y0: Y_LBL,
|
43
|
+
x1: _x1_expression,
|
44
|
+
y1: Y_LBL
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Expression to evaluate x1 point.
|
50
|
+
#
|
51
|
+
def _x1_expression
|
52
|
+
AbstractToken::MathSub.new(
|
53
|
+
left: AbstractToken::MathAdd.new(
|
54
|
+
left: X_LBL,
|
55
|
+
right: READ_VAR
|
56
|
+
),
|
57
|
+
right: 1
|
58
|
+
)
|
59
|
+
end
|
60
|
+
|
61
|
+
#
|
62
|
+
# Full-length line pattern, i.e. a line that fills
|
63
|
+
# the whole scan line.
|
64
|
+
#
|
65
|
+
# Formatted for the horizontal scan lines.
|
66
|
+
#
|
67
|
+
def _full_line_pattern
|
68
|
+
AbstractToken::DrawLineByAbsCoords.new(
|
69
|
+
x0: X_LBL,
|
70
|
+
y0: Y_LBL,
|
71
|
+
x1: @segment_size,
|
72
|
+
y1: Y_LBL
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,304 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ImgToScript
|
4
|
+
module Generators
|
5
|
+
module RunLengthEncoding
|
6
|
+
#
|
7
|
+
# Base class for the RLE-based script generators.
|
8
|
+
#
|
9
|
+
class RunLengthEncoding < Generator
|
10
|
+
private
|
11
|
+
|
12
|
+
#
|
13
|
+
# Encode pixels into the RLE data.
|
14
|
+
#
|
15
|
+
def _encode_pixels
|
16
|
+
@run_length_data = RunLengthEncodingRb.encode(@pixels)
|
17
|
+
run_lengths = []
|
18
|
+
|
19
|
+
encode = lambda { |count:, pixel:|
|
20
|
+
count = -count if pixel.to_color == "white"
|
21
|
+
run_lengths.push(count.to_s)
|
22
|
+
}
|
23
|
+
|
24
|
+
@run_length_data.each { |e| encode.call(pixel: e.chunk, count: e.run_length) }
|
25
|
+
|
26
|
+
_append_data(run_lengths)
|
27
|
+
end
|
28
|
+
|
29
|
+
def _append_data(arr)
|
30
|
+
@tokens.append(
|
31
|
+
AbstractToken::DataStore.new(
|
32
|
+
data: arr,
|
33
|
+
require_nl: true
|
34
|
+
)
|
35
|
+
)
|
36
|
+
end
|
37
|
+
|
38
|
+
#
|
39
|
+
# Append RLE decoder procedure afther the DATA sequence.
|
40
|
+
#
|
41
|
+
def _append_decoder
|
42
|
+
_dec_line01
|
43
|
+
_dec_line02
|
44
|
+
_dec_line03
|
45
|
+
_dec_line04
|
46
|
+
_dec_line05
|
47
|
+
_dec_line06
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# 1-st line of the decoder procedure.
|
52
|
+
#
|
53
|
+
# In this line the X & Y variables are defined. The X and Y variables
|
54
|
+
# are used to keep track of the current position on the screen.
|
55
|
+
#
|
56
|
+
# Then the main loop starts. The loop iterates over each pixel value
|
57
|
+
# (represented by the variable READ_VAR) in the input data and translates
|
58
|
+
# the RLE-encoded values into the "draw a line" commands that draw lines
|
59
|
+
# on the screen.
|
60
|
+
#
|
61
|
+
def _dec_line01
|
62
|
+
_init_x
|
63
|
+
_init_y
|
64
|
+
_start_loop
|
65
|
+
_read_value
|
66
|
+
end
|
67
|
+
|
68
|
+
#
|
69
|
+
# 2-nd line of the decoder procedure.
|
70
|
+
#
|
71
|
+
# The IF statement is used to check cases where the line would extend
|
72
|
+
# beyond the bounds of the image/screen. In this case the program jumps
|
73
|
+
# to the 5-th line (3 lines down from the current line) of the decoder,
|
74
|
+
# that handles this edge case.
|
75
|
+
#
|
76
|
+
def _dec_line02
|
77
|
+
expression = AbstractToken::MathGreaterThan.new(
|
78
|
+
left: _sum_of_current_point_and_length,
|
79
|
+
right: @segment_size
|
80
|
+
)
|
81
|
+
|
82
|
+
@tokens.append(
|
83
|
+
AbstractToken::IfCondition.new(
|
84
|
+
expression: expression,
|
85
|
+
consequent: CurrentLinePlaceholder.new(3),
|
86
|
+
require_nl: true
|
87
|
+
)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# 3-rd line of the decoder procedure.
|
93
|
+
#
|
94
|
+
# Draws a line if S is a positive number. Positive numbers represent
|
95
|
+
# black pixels. Negative numbers represent white pixels.
|
96
|
+
#
|
97
|
+
def _dec_line03
|
98
|
+
@tokens.append(
|
99
|
+
AbstractToken::IfCondition.new(
|
100
|
+
expression: _read_var_positive,
|
101
|
+
consequent: @part_line_pattern,
|
102
|
+
require_nl: true
|
103
|
+
)
|
104
|
+
)
|
105
|
+
end
|
106
|
+
|
107
|
+
#
|
108
|
+
# 4-th line of the decoder procedure.
|
109
|
+
#
|
110
|
+
# Updates major axis position. Ends the main loop. Jumps to the end
|
111
|
+
# of the program.
|
112
|
+
#
|
113
|
+
def _dec_line04
|
114
|
+
_update_current_point
|
115
|
+
_end_loop
|
116
|
+
_jump_to_the_end
|
117
|
+
end
|
118
|
+
|
119
|
+
#
|
120
|
+
# 5-th line of the decoder procedure.
|
121
|
+
#
|
122
|
+
# This part handles cases where the line would extend beyond the
|
123
|
+
# bounds of the image/screen. It draws a line from the current
|
124
|
+
# position up to the end of the image/screen.
|
125
|
+
#
|
126
|
+
def _dec_line05
|
127
|
+
@tokens.append(
|
128
|
+
AbstractToken::IfCondition.new(
|
129
|
+
expression: _read_var_positive,
|
130
|
+
consequent: @full_line_pattern,
|
131
|
+
require_nl: true
|
132
|
+
)
|
133
|
+
)
|
134
|
+
end
|
135
|
+
|
136
|
+
#
|
137
|
+
# 6-th line of the decoder procedure.
|
138
|
+
#
|
139
|
+
# This part handles cases where the line would extend beyond the
|
140
|
+
# bounds of the image/screen. It updates the current position on
|
141
|
+
# the screen and the run-length value. When it jumps back to the
|
142
|
+
# line length check (2nd line of the decoder).
|
143
|
+
#
|
144
|
+
def _dec_line06
|
145
|
+
_increment_minor_axis
|
146
|
+
_update_read_variable
|
147
|
+
_reset_major_axis
|
148
|
+
_jump_to_length_check
|
149
|
+
end
|
150
|
+
|
151
|
+
def _read_var_positive
|
152
|
+
AbstractToken::MathGreaterThan.new(
|
153
|
+
left: READ_VAR,
|
154
|
+
right: 0
|
155
|
+
)
|
156
|
+
end
|
157
|
+
|
158
|
+
def _init_x
|
159
|
+
@tokens.append(
|
160
|
+
AbstractToken::AssignValue.new(
|
161
|
+
left: X_LBL,
|
162
|
+
right: @x_offset,
|
163
|
+
require_nl: true
|
164
|
+
)
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
def _init_y
|
169
|
+
@tokens.append(
|
170
|
+
AbstractToken::AssignValue.new(
|
171
|
+
left: Y_LBL,
|
172
|
+
right: @y_offset
|
173
|
+
)
|
174
|
+
)
|
175
|
+
end
|
176
|
+
|
177
|
+
def _start_loop
|
178
|
+
@tokens.append(
|
179
|
+
AbstractToken::LoopBegin.new(
|
180
|
+
var_name: LOOP_VAR,
|
181
|
+
start_value: 1,
|
182
|
+
end_value: @run_length_data.length
|
183
|
+
)
|
184
|
+
)
|
185
|
+
end
|
186
|
+
|
187
|
+
def _read_value
|
188
|
+
@tokens.append(
|
189
|
+
AbstractToken::DataRead.new(
|
190
|
+
var_list: READ_VAR
|
191
|
+
)
|
192
|
+
)
|
193
|
+
end
|
194
|
+
|
195
|
+
def _abs_length_value
|
196
|
+
AbstractToken::AbsFunc.new(
|
197
|
+
expression: READ_VAR
|
198
|
+
)
|
199
|
+
end
|
200
|
+
|
201
|
+
def _sum_of_current_point_and_length
|
202
|
+
AbstractToken::MathAdd.new(
|
203
|
+
left: _abs_length_value,
|
204
|
+
right: @major_axis_symbol
|
205
|
+
)
|
206
|
+
end
|
207
|
+
|
208
|
+
def _update_current_point
|
209
|
+
@tokens.append(
|
210
|
+
AbstractToken::AssignValue.new(
|
211
|
+
left: @major_axis_symbol,
|
212
|
+
right: _sum_of_current_point_and_length,
|
213
|
+
require_nl: true
|
214
|
+
)
|
215
|
+
)
|
216
|
+
end
|
217
|
+
|
218
|
+
def _end_loop
|
219
|
+
@tokens.append(
|
220
|
+
AbstractToken::LoopEnd.new(
|
221
|
+
var_name: LOOP_VAR
|
222
|
+
)
|
223
|
+
)
|
224
|
+
end
|
225
|
+
|
226
|
+
def _jump_to_the_end
|
227
|
+
@tokens.append(
|
228
|
+
AbstractToken::GoTo.new(
|
229
|
+
line: CurrentLinePlaceholder.new(3)
|
230
|
+
)
|
231
|
+
)
|
232
|
+
end
|
233
|
+
|
234
|
+
def _sign_func
|
235
|
+
AbstractToken::SignFunc.new(
|
236
|
+
expression: READ_VAR
|
237
|
+
)
|
238
|
+
end
|
239
|
+
|
240
|
+
def _increment_minor_axis
|
241
|
+
@tokens.append(
|
242
|
+
AbstractToken::AssignValue.new(
|
243
|
+
left: @minor_axis_symbol,
|
244
|
+
right: AbstractToken::MathAdd.new(
|
245
|
+
left: @minor_axis_symbol,
|
246
|
+
right: 1
|
247
|
+
),
|
248
|
+
require_nl: true
|
249
|
+
)
|
250
|
+
)
|
251
|
+
end
|
252
|
+
|
253
|
+
def _reset_major_axis
|
254
|
+
@tokens.append(
|
255
|
+
AbstractToken::AssignValue.new(
|
256
|
+
left: @major_axis_symbol,
|
257
|
+
right: @major_axis_value
|
258
|
+
)
|
259
|
+
)
|
260
|
+
end
|
261
|
+
|
262
|
+
def _jump_to_length_check
|
263
|
+
@tokens.append(
|
264
|
+
AbstractToken::GoTo.new(
|
265
|
+
line: CurrentLinePlaceholder.new(-4)
|
266
|
+
)
|
267
|
+
)
|
268
|
+
end
|
269
|
+
|
270
|
+
def _sum_token
|
271
|
+
AbstractToken::MathAdd.new(
|
272
|
+
left: _abs_length_value,
|
273
|
+
right: @major_axis_symbol
|
274
|
+
)
|
275
|
+
end
|
276
|
+
|
277
|
+
def _sub_token
|
278
|
+
AbstractToken::MathSub.new(
|
279
|
+
left: _sum_token,
|
280
|
+
right: @image_size
|
281
|
+
)
|
282
|
+
end
|
283
|
+
|
284
|
+
def _mult_token
|
285
|
+
AbstractToken::MathMult.new(
|
286
|
+
left: AbstractToken::Parentheses.new(
|
287
|
+
expression: _sub_token
|
288
|
+
),
|
289
|
+
right: _sign_func
|
290
|
+
)
|
291
|
+
end
|
292
|
+
|
293
|
+
def _update_read_variable
|
294
|
+
@tokens.append(
|
295
|
+
AbstractToken::AssignValue.new(
|
296
|
+
left: READ_VAR,
|
297
|
+
right: _mult_token
|
298
|
+
)
|
299
|
+
)
|
300
|
+
end
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
304
|
+
end
|