barkest_lcd 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,126 @@
1
+
2
+ BarkestLcd::PicoLcdGraphic.class_eval do
3
+
4
+ init_hook :init_flasher
5
+
6
+
7
+ ##
8
+ # Delay for commands.
9
+ COMMAND_DELAY = 0x64
10
+
11
+
12
+ ##
13
+ # Gets the current mode of the device.
14
+ attr_reader :mode
15
+
16
+
17
+ ##
18
+ # Is the device currently functioning as a flasher?
19
+ def is_flasher?
20
+ (mode == :flasher)
21
+ end
22
+
23
+
24
+ ##
25
+ # Switch between keyboard and flasher mode.
26
+ def switch_mode
27
+ next_mode = (mode == :keyboard) ? :flasher : :keyboard
28
+ mode = :switching
29
+
30
+ if next_mode == :flasher
31
+ write [ HID_REPORT.EXIT_KEYBOARD, timeout & 0xFF, (timeout >> 8) & 0xFF ]
32
+ else
33
+ write [ HID_REPORT.EXIT_FLASHER, timeout & 0xFF, (timeout >> 8) & 0xFF ]
34
+ end
35
+
36
+ loop_while { mode == :switching }
37
+
38
+ self
39
+ end
40
+
41
+
42
+ private
43
+
44
+
45
+ def init_flasher(_)
46
+ @mode = :keyboard
47
+
48
+ input_hook(HID_REPORT.EXIT_FLASHER) do |_,_,data|
49
+ data = data.getbyte(0)
50
+ if data == 0
51
+ @mode = :keyboard
52
+ HIDAPI.debug 'switch to KEYBOARD mode'
53
+ else
54
+ @mode = :unknown
55
+ log_error data, 'HID_REPORT.EXIT_FLASHER failed'
56
+ end
57
+ end
58
+
59
+ input_hook(HID_REPORT.EXIT_KEYBOARD) do |_,_,data|
60
+ data = data.getbyte(0)
61
+ if data == 0
62
+ @mode = :flasher
63
+ HIDAPI.debug 'switched to FLASHER mode'
64
+ else
65
+ @mode = :unknown
66
+ log_error data, 'HID_REPORT.EXIT_KEYBOARD failed'
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+ def flash_is_enabled?(type)
73
+ if is_flasher?
74
+ return (FLASH_TYPE.keys.include?(type) || FLASH_TYPE.values.include?(type))
75
+ end
76
+ false
77
+ end
78
+
79
+ def get_flash_write_message(type)
80
+ if is_flasher?
81
+ return case type
82
+ when :CODE_MEMORY, FLASH_TYPE.CODE_MEMORY
83
+ FLASH_REPORT.WRITE_MEMORY
84
+
85
+ when :CODE_SPLASH, FLASH_TYPE.CODE_SPLASH
86
+ KEYBD_REPORT.WRITE_MEMORY
87
+
88
+ when :EPROM_EXTERNAL, FLASH_TYPE.EPROM_EXTERNAL
89
+ OUT_REPORT.EXT_EE_WRITE
90
+
91
+ when :EPROM_INTERNAL, FLASH_TYPE.EPROM_INTERNAL
92
+ OUT_REPORT.INT_EE_WRITE
93
+
94
+ else
95
+ 0
96
+ end
97
+ end
98
+ 0
99
+ end
100
+
101
+ def get_flash_read_message(type)
102
+ if is_flasher?
103
+ return case type
104
+ when :CODE_MEMORY, FLASH_TYPE.CODE_MEMORY
105
+ FLASH_REPORT.READ_MEMORY
106
+
107
+ when :CODE_SPLASH, FLASH_TYPE.CODE_SPLASH
108
+ KEYBD_REPORT.READ_MEMORY
109
+
110
+ when :EPROM_EXTERNAL, FLASH_TYPE.EPROM_EXTERNAL
111
+ OUT_REPORT.EXT_EE_READ
112
+
113
+ when :EPROM_INTERNAL, FLASH_TYPE.EPROM_INTERNAL
114
+ OUT_REPORT.INT_EE_READ
115
+
116
+ else
117
+ 0
118
+ end
119
+ end
120
+ 0
121
+ end
122
+
123
+
124
+
125
+
126
+ end
@@ -0,0 +1,31 @@
1
+ BarkestLcd::PicoLcdGraphic.class_eval do
2
+
3
+ init_hook :init_ir
4
+
5
+ ##
6
+ # Sets the code to run when IR data is received.
7
+ #
8
+ # Yields the bytes received as a string.
9
+ def on_ir_data(&block)
10
+ raise ArgumentError, 'Missing block.' unless block_given?
11
+ @on_ir_data = block
12
+ self
13
+ end
14
+
15
+ private
16
+
17
+ def init_ir(_)
18
+ @on_ir_data = nil
19
+ input_hook IN_REPORT.IR_DATA do |_, _, data|
20
+ process_ir_data data
21
+ end
22
+ end
23
+
24
+ def process_ir_data(data)
25
+ HIDAPI.debug "IR data: #{data.inspect}"
26
+ if @on_ir_data
27
+ @on_ir_data.call(data)
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,92 @@
1
+ BarkestLcd::PicoLcdGraphic.class_eval do
2
+
3
+ init_hook :init_key
4
+
5
+
6
+ ##
7
+ # Sets the code to run when a key is pressed down.
8
+ #
9
+ # Yields the key number as a single byte.
10
+ def on_key_down(&block)
11
+ raise ArgumentError, 'Missing block.' unless block_given?
12
+ @on_key_down = block
13
+ self
14
+ end
15
+
16
+
17
+ ##
18
+ # Sets the code to run when a key is released.
19
+ #
20
+ # Yields the key number as a single byte.
21
+ def on_key_up(&block)
22
+ raise ArgumentError, 'Missing block.' unless block_given?
23
+ @on_key_up = block
24
+ self
25
+ end
26
+
27
+
28
+ ##
29
+ # Gets the state of a specific key.
30
+ def key_state(key)
31
+ return false unless key
32
+ return false if key <= 0
33
+ return false if @keys.length <= key
34
+ @keys[key]
35
+ end
36
+
37
+
38
+ private
39
+
40
+
41
+ def init_key(_)
42
+ @keys = []
43
+ @on_key_up = @on_key_down = nil
44
+
45
+ # Hook the keyboard and IR data events.
46
+ input_hook IN_REPORT.KEY_STATE do |_, _, data|
47
+ if data.length < 2
48
+ log_error 2, 'not enough data for IN_REPORT.KEY_STATE'
49
+ else
50
+ process_key_state data
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ def process_key_state(data)
57
+ key1 = data.length >= 1 ? (data.getbyte(0) & 0xFF) : 0
58
+ key2 = data.length >= 2 ? (data.getbyte(1) & 0xFF) : 0
59
+
60
+ # make sure the array is big enough to represent the largest reported key.
61
+ max = (key1 < key2 ? key2 : key1) + 1
62
+ if @keys.length < max
63
+ @keys += [nil] * (max - @keys.length)
64
+ end
65
+
66
+ HIDAPI.debug "Pressed keys: #{key1} #{key2}"
67
+
68
+ # go through the array and process changes.
69
+ @keys.each_with_index do |state,index|
70
+ unless index == 0
71
+ if state && key1 != index && key2 != index
72
+ # key was pressed but is not one of the currently pressed keys.
73
+ if @on_key_up
74
+ @on_key_up.call(index)
75
+ end
76
+ @keys[index] = false
77
+ end
78
+ if key1 == index || key2 == index
79
+ unless state
80
+ # key was not pressed before but is one of the currently pressed keys.
81
+ if @on_key_down
82
+ @on_key_down.call(index)
83
+ end
84
+ @keys[index] = true
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ end
92
+
@@ -0,0 +1,40 @@
1
+ BarkestLcd::PicoLcdGraphic.class_eval do
2
+
3
+ init_hook :init_splash
4
+
5
+ ##
6
+ # Gets the size for the splash storage.
7
+ def splash_size(refresh = false)
8
+ @splash_size = nil if refresh
9
+
10
+ if @splash_size.nil?
11
+ write [ HID_REPORT.GET_MAX_STX_SIZE ]
12
+ loop_while { @splash_size.nil? }
13
+ end
14
+
15
+ { size: @splash_size, max: @splash_max_size }
16
+ end
17
+
18
+
19
+
20
+ private
21
+
22
+ def init_splash(_)
23
+ @splash_size = nil
24
+ @splash_max_size = nil
25
+
26
+ input_hook(HID_REPORT.GET_MAX_STX_SIZE) do |_,_,data|
27
+ if data.length < 4
28
+ @splash_size = 0
29
+ @splash_max_size = 0
30
+ log_error 2, 'not enough data for HID_REPORT.GET_MAX_STX_SIZE'
31
+ else
32
+ @splash_max_size = ((data.getbyte(1) << 8) & 0xFF00) | (data.getbyte(0) & 0xFF)
33
+ @splash_size = ((data.getbyte(3) << 8) & 0xFF00) | (data.getbyte(2) & 0xFF)
34
+ HIDAPI.debug "splash size=#{@splash_size}, max=#{@splash_max_size}"
35
+ end
36
+ end
37
+
38
+ end
39
+
40
+ end
@@ -0,0 +1,40 @@
1
+ BarkestLcd::PicoLcdGraphic.class_eval do
2
+
3
+ init_hook :init_version
4
+
5
+ ##
6
+ # Gets the version of this device.
7
+ def version(refresh = false)
8
+ @version = nil if refresh
9
+
10
+ unless @version
11
+ @version = []
12
+ write [ HID_REPORT.GET_VERSION_1 ]
13
+ loop_while { @version.empty? }
14
+ end
15
+
16
+ @version
17
+ end
18
+
19
+ private
20
+
21
+ def process_version(type, data)
22
+ if data.length < 2
23
+ log_error 2, "not enough data for HID_REPORT.#{HID_REPORT.key(type)}"
24
+ @version[0] = 0
25
+ @version[1] = 0
26
+ else
27
+ @version[0] = data.getbyte(1)
28
+ @version[1] = data.getbyte(0)
29
+ end
30
+ end
31
+
32
+ def init_version(_)
33
+ @version = nil
34
+ input_hook(HID_REPORT.GET_VERSION_1) do |_,type,data|
35
+ process_version(type, data)
36
+ end
37
+ end
38
+
39
+
40
+ end
@@ -0,0 +1,467 @@
1
+ require 'models/font'
2
+
3
+ module BarkestLcd
4
+ ##
5
+ # Adds some simple graphics capabilities to a model.
6
+ module SimpleGraphic
7
+
8
+ ##
9
+ # An error occurring when an invalid 'x' or 'y' coordinate is specified.
10
+ InvalidPosition = Class.new(StandardError)
11
+
12
+ public
13
+
14
+ ##
15
+ # Gets the width of the graphic object.
16
+ def width
17
+ @graphic_width ||= 0
18
+ end
19
+
20
+ ##
21
+ # Gets the height of the graphic object.
22
+ def height
23
+ @graphic_height ||= 0
24
+ end
25
+
26
+ ##
27
+ # Gets a snapshot of the graphic data.
28
+ def graphic_data_snapshot(x = nil, y = nil, w = nil, h = nil)
29
+ if x.nil? && y.nil? && w.nil? && h.nil?
30
+ return @graphic_data.map{|row| row.dup.freeze}.freeze
31
+ end
32
+
33
+ x ||= 0
34
+ y ||= 0
35
+ w ||= width - x
36
+ h ||= height - y
37
+
38
+ return [] if h < 1
39
+ return [ [] * h ] if w < 1
40
+
41
+ ret = []
42
+ row = [ false ] * w
43
+ h.times { ret << row.dup }
44
+
45
+ y2 = y + h - 1
46
+ x2 = x + w - 1
47
+ (y..y2).each do |source_y|
48
+ if source_y >= 0 && source_y < height
49
+ dest_y = source_y - y
50
+ dest_row = ret[dest_y]
51
+ source_row = @graphic_data[source_y]
52
+ (x..x2).each do |source_x|
53
+ if source_x >= 0 && source_x < width
54
+ dest_x = source_x - x
55
+ dest_row[dest_x] = source_row[source_x]
56
+ end
57
+ end
58
+ end
59
+ end
60
+
61
+ ret.map{|r| r.freeze}.freeze
62
+ end
63
+
64
+ ##
65
+ # Is the graphic object dirty?
66
+ def dirty?
67
+ @graphic_dirty
68
+ end
69
+
70
+ ##
71
+ # Low level function to set a single bit.
72
+ def set_bit(x, y, bit = true)
73
+ raise BarkestLcd::SimpleGraphic::InvalidPosition, "'x' must be between 0 and #{width - 1}" if x < 0 || x >= width
74
+ raise BarkestLcd::SimpleGraphic::InvalidPosition, "'y' must be between 0 and #{height - 1}" if y < 0 || y >= height
75
+ unless @graphic_data[y][x] == bit
76
+ @graphic_data[y][x] = bit
77
+ @graphic_dirty = true
78
+ end
79
+ self
80
+ end
81
+
82
+ ##
83
+ # Low level function to get a single bit.
84
+ def get_bit(x, y)
85
+ return false if x < 0 || x >= width
86
+ return false if y < 0 || y >= height
87
+ @graphic_data[y][x]
88
+ end
89
+
90
+ ##
91
+ # Clears the screen.
92
+ def clear(bit = false)
93
+ row_data = [bit] * width
94
+ (0...height).each do |y|
95
+ @graphic_data[y] = row_data.dup
96
+ end
97
+ @graphic_dirty = true
98
+ self
99
+ end
100
+
101
+ ##
102
+ # Draws a horizontal line.
103
+ def draw_hline(y, start_x, end_x, bit = true)
104
+ raise BarkestLcd::SimpleGraphic::InvalidPosition, "'y' must be between 0 and #{height - 1}" if y < 0 || y >= height
105
+ row = @graphic_data[y]
106
+ (start_x..end_x).each do |x|
107
+ if x >= 0 && x < width
108
+ unless row[x] == bit
109
+ row[x] = bit
110
+ @graphic_dirty = true
111
+ end
112
+ end
113
+ end
114
+ self
115
+ end
116
+
117
+ ##
118
+ # Draws a vertical line.
119
+ def draw_vline(x, start_y, end_y, bit = true)
120
+ raise BarkestLcd::SimpleGraphic::InvalidPosition, "'x' must be between 0 and #{width - 1}" if x < 0 || x >= width
121
+ (start_y..end_y).each do |y|
122
+ if y >= 0 && y < height
123
+ unless @graphic_data[y][x] == bit
124
+ @graphic_data[y][x] = bit
125
+ @graphic_dirty = true
126
+ end
127
+ end
128
+ end
129
+ self
130
+ end
131
+
132
+ ##
133
+ # Draws a line.
134
+ def draw_line(start_x, start_y, end_x, end_y, bit = true)
135
+ if start_y == end_y
136
+ draw_hline(start_y, start_x, end_x, bit)
137
+ elsif start_x == end_x
138
+ draw_vline(start_x, start_y, end_y, bit)
139
+ else
140
+ # slope
141
+ m = (end_y - start_y).to_f / (end_x - start_x).to_f
142
+
143
+ # and y_offset
144
+ b = start_y - (m * start_x)
145
+
146
+ (start_x..end_x).each do |x|
147
+ # simple rounding, add 0.5 and trim to an integer.
148
+ x = x.to_i
149
+ y = ((m * x) + b + 0.5).to_i
150
+ if x >= 0 && x < width && y >= 0 && y < height
151
+ unless @graphic_data[y][x] == bit
152
+ @graphic_data[y][x] = bit
153
+ @graphic_dirty = true
154
+ end
155
+ end
156
+ end
157
+ end
158
+ self
159
+ end
160
+
161
+ ##
162
+ # Draws a rectangle.
163
+ def draw_rect(x, y, w, h, bit = true)
164
+ raise ArgumentError, '\'w\' must be at least 1' if w < 1
165
+ raise ArgumentError, '\'h\' must be at least 1' if h < 1
166
+ x2 = x + w - 1
167
+ y2 = y + h - 1
168
+ draw_hline(y, x, x2, bit) if y >= 0 && y <= height
169
+ draw_hline(y2, x, x2, bit) if y2 >= 0 && y2 <= height
170
+ draw_vline(x, y, y2, bit) if x >= 0 && x <= width
171
+ draw_vline(x2, y, y2, bit) if x2 >= 0 && x2 <= width
172
+ self
173
+ end
174
+
175
+ ##
176
+ # Draws a filled rectangle.
177
+ def fill_rect(x, y, w, h, bit = true)
178
+ raise ArgumentError, '\'w\' must be at least 1' if w < 1
179
+ raise ArgumentError, '\'h\' must be at least 1' if h < 1
180
+ x2 = x + w - 1
181
+ y2 = y + h - 1
182
+ (y..y2).each do |dest_y|
183
+ if dest_y >= 0 && dest_y < height
184
+ dest_row = @graphic_data[dest_y]
185
+ (x..x2).each do |dest_x|
186
+ if dest_x >= 0 && dest_x < width
187
+ unless dest_row[dest_x] == bit
188
+ dest_row[dest_x] = bit
189
+ @graphic_dirty = true
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ self
196
+ end
197
+
198
+ ##
199
+ # Copies data directly to the graphic.
200
+ #
201
+ # The +data+ parameter is special and can follow one of two paths.
202
+ #
203
+ # If no block is provided, then +data+ must be a two dimensional array of boolean or integers.
204
+ #
205
+ # If a block is provided, then +data+ is passed to the block as the third parameter. If +data+ is nil
206
+ # then the third parameter is nil. The block will receive the current x and y offset as the first two
207
+ # parameters. The block must return a boolean value, true to set a pixel or false to clear it. It can
208
+ # also return nil to leave a pixel as is.
209
+ #
210
+ # :yields: +offset_x+, +offset_y+, and +data+ to the block which must return a boolean.
211
+ #
212
+ # blit(10, 10, 20, 5, my_data) do |offset_x, offset_y, data|
213
+ # # offset_x will be in the range (0...20)
214
+ # # offset_y will be in the range (0...5)
215
+ # data.is_pixel_set?(offset_x, offset_y)
216
+ # end
217
+ #
218
+ def blit(x, y, w, h, data = nil, &block)
219
+ raise ArgumentError, 'data is required unless a block is provided' if data.nil? && !block_given?
220
+ raise ArgumentError, '\'w\' must be at least 1' if w < 1
221
+ raise ArgumentError, '\'h\' must be at least 1' if h < 1
222
+
223
+ x2 = x + w - 1
224
+ y2 = y + h - 1
225
+
226
+ # The default block simply looks up the bit in the data and returns true if the bit is true or 1.
227
+ unless block_given?
228
+ block = Proc.new do |b_x, b_y, b_data|
229
+ b_bit = b_data[b_y][b_x]
230
+ b_bit == true || b_bit == 1
231
+ end
232
+ end
233
+
234
+ (y..y2).each do |dest_y|
235
+ if dest_y >= 0 && dest_y < height
236
+ dest_row = @graphic_data[dest_y]
237
+ (x..x2).each do |dest_x|
238
+ if dest_x >= 0 && dest_x < width
239
+ offset_x = dest_x - x
240
+ offset_y = dest_y - y
241
+ bit = block.call(offset_x, offset_y, data)
242
+ unless bit.nil?
243
+ unless dest_row[dest_x] == bit
244
+ dest_row[dest_x] = bit
245
+ @graphic_dirty = true
246
+ end
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end
252
+
253
+ self
254
+ end
255
+
256
+ ##
257
+ # Attribute used to store the horizontal text offset after the last call to draw_text.
258
+ attr_accessor :text_offset_left
259
+
260
+ ##
261
+ # Attribute used to store the vertical text offset after the last call to draw_text.
262
+ attr_accessor :text_offset_bottom
263
+
264
+ ##
265
+ # Draws a string of text to the graphic.
266
+ #
267
+ # The text will not wrap.
268
+ #
269
+ # Options:
270
+ # * x - The horizontal position for the text. Defaults to +text_offset_left+.
271
+ # * y - The vertical position for the text. Defaults to aligning the bottom with +text_offset_bottom+.
272
+ # * bold - Defaults to false.
273
+ # * bit - Defaults to true.
274
+ def draw_text(text, options = {})
275
+
276
+ if text.include?("\n")
277
+ if options[:y]
278
+ self.text_offset_bottom = options[:y]
279
+ else
280
+ font = options[:bold] ? BarkestLcd::Font.bold : BarkestLcd::Font.regular
281
+ self.text_offset_bottom = text_offset_bottom ? (text_offset_bottom - font.height) : 0
282
+ end
283
+ options[:x] ||= (text_offset_left || 0)
284
+ text.split("\n").each do |line|
285
+ draw_text(line, options.merge( { y: text_offset_bottom }))
286
+ end
287
+ return self
288
+ end
289
+
290
+ x = options.delete(:x)
291
+ y = options.delete(:y)
292
+
293
+ bold = options.delete(:bold)
294
+ bold = false if bold.nil?
295
+ bit = options.delete(:bit)
296
+ bit = true if bit.nil?
297
+ char_spacing = options.delete(:char_spacing) || -1
298
+
299
+ font = bold ? BarkestLcd::Font.bold : BarkestLcd::Font.regular
300
+
301
+ max_h = font.height
302
+
303
+ x ||= text_offset_left || 0
304
+
305
+ # we'll be aligning the bottoms of the glyphs.
306
+ if y
307
+ bottom = y + max_h
308
+ else
309
+ bottom = text_offset_bottom || max_h
310
+ end
311
+
312
+ # store the bottom offset and left offset for future use.
313
+ self.text_offset_bottom = bottom
314
+ self.text_offset_left = x
315
+
316
+ text = text.to_s
317
+ return self if text == ''
318
+
319
+ glyphs = font.glyphs(text)
320
+
321
+ glyphs.each do |glyph|
322
+ if x > width # no need to continue.
323
+ # update the left offset.
324
+ self.text_offset_left = x
325
+ return self
326
+ end
327
+ if glyph.width > 0 && glyph.height > 0
328
+ y = bottom - glyph.height
329
+ blit(x, y, glyph.width, glyph.height, glyph.data) { |_x, _y, _data| _data[_y][_x] ? bit : nil }
330
+ end
331
+ x += glyph.width > 0 ? (glyph.width + char_spacing) : 0
332
+ end
333
+
334
+ # update the left offset.
335
+ self.text_offset_left = x
336
+
337
+ self
338
+ end
339
+
340
+ ##
341
+ # Draws a string of text to a confined region of the graphic.
342
+ #
343
+ # Options:
344
+ # * bold - Defaults to false.
345
+ # * bit - Defaults to true.
346
+ # * align - Can be :left, :center, or :right. Defaults to :left.
347
+ # * border - Defaults to false.
348
+ # * fill - Defaults to false.
349
+ #
350
+ def draw_text_box(text, x, y, w, h, options = {})
351
+ bold = options.delete(:bold)
352
+ bold = false if bold.nil?
353
+ bit = options.delete(:bit)
354
+ bit = true if bit.nil?
355
+ align = options.delete(:align)
356
+ align = :left if align.nil?
357
+ border = options.delete(:border)
358
+ border = false if border.nil?
359
+ fill = options.delete(:fill)
360
+ fill = false if fill.nil?
361
+ char_spacing = options.delete(:char_spacing) || -1
362
+
363
+ raise ArgumentError, '\'w\' must be at least 1' if w < 1
364
+ raise ArgumentError, '\'h\' must be at least 1' if h < 1
365
+
366
+ if fill
367
+ fill_rect(x, y, w, h, !bit)
368
+ end
369
+
370
+ if border && w > 2
371
+ x2 = x + w - 1
372
+ draw_vline(x, y, y + h - 1, bit) if x >= 0 && x < width
373
+ draw_vline(x2, y, y + h - 1, bit) if x2 >= 0 && x2 < width
374
+ w -= 2
375
+ x += 1
376
+ end
377
+
378
+ if border && h > 2
379
+ y2 = y + h - 1
380
+ draw_hline(y, x, x + w - 1, bit) if y >= 0 && y < height
381
+ draw_hline(y2, x, x + w - 1, bit) if y2 >= 0 && y2 < height
382
+ h -= 2
383
+ y += 1
384
+ end
385
+
386
+ text = text.to_s
387
+ return self if text == ''
388
+
389
+ # We'll use an anonymous class to buffer the text box.
390
+ box = Class.new do
391
+ include BarkestLcd::SimpleGraphic
392
+ def initialize(w,h)
393
+ init_graphic w, h
394
+ end
395
+ end.new(w, h)
396
+
397
+ # copy the current contents of the area into the box.
398
+ box.blit(0, 0, w, h, graphic_data_snapshot(x, y, w, h))
399
+
400
+ font = bold ? BarkestLcd::Font.bold : BarkestLcd::Font.regular
401
+
402
+ # measure the text and split it into lines, grab the lines, ignore the size.
403
+ text_lines = font.measure(text, w)[2]
404
+
405
+ text_lines.each do |line|
406
+ lx = 0
407
+ if align == :center
408
+ lw = font.measure(line)[0]
409
+ lx = (w - lw) / 2
410
+ elsif align == :right
411
+ lw = font.measure(line)[0]
412
+ lx = (w - lw)
413
+ end
414
+ # each line uses the bottom offset from the previous line as the top of the current line.
415
+ box.draw_text(line, x: lx, y: box.text_offset_bottom, bold: bold, bit: bit, char_spacing: char_spacing)
416
+ break if box.text_offset_bottom > h
417
+ end
418
+
419
+ # copy the contents of the box back into the current graphic.
420
+ blit(x, y, w, h, box.graphic_data_snapshot)
421
+
422
+ self
423
+ end
424
+
425
+ protected
426
+
427
+ def init_graphic(width, height)
428
+ @graphic_width = width < 4 ? 4 : width
429
+ @graphic_height = height < 4 ? 4 : height
430
+ row_data = [false] * width
431
+ @graphic_data = []
432
+ @graphic_height.times { @graphic_data << row_data.dup }
433
+ @graphic_dirty = true
434
+ @graphic_clean_data = nil
435
+ end
436
+
437
+ def clear_dirty
438
+ @graphic_dirty = false
439
+ @graphic_clean_data = []
440
+ @graphic_height.times { |line| @graphic_clean_data << @graphic_data[line].dup }
441
+ end
442
+
443
+ def dirty_rect?(x, y, w, h)
444
+ raise InvalidPosition, "'x' must be between 0 and #{width - 1}" if x < 0 || x >= width
445
+ raise InvalidPosition, "'y' must be between 0 and #{height - 1}" if y < 0 || y >= height
446
+ raise InvalidPosition, "'w' cannot be less than 1" if w < 1
447
+ raise InvalidPosition, "'h' cannot be less than 1" if h < 1
448
+ x2 = x + w - 1
449
+ y2 = y + h - 1
450
+ raise InvalidPosition, "'w' must not be greater than #{width} - 'x'" if x2 >= width
451
+ raise InvalidPosition, "'h' must not be greater than #{height} - 'y'" if y2 >= height
452
+
453
+ return true unless @graphic_clean_data
454
+
455
+ (y..y2).each do |yy|
456
+ clean_row = @graphic_clean_data[yy]
457
+ test_row = @graphic_data[yy]
458
+ (x..x2).each do |xx|
459
+ return true unless test_row[xx] == clean_row[xx]
460
+ end
461
+ end
462
+
463
+ false
464
+ end
465
+
466
+ end
467
+ end