tui-td 0.2.6 → 0.2.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,6 +2,8 @@
2
2
 
3
3
  require "chunky_png"
4
4
  require_relative "ansi_utils"
5
+ require_relative "cairo_renderer"
6
+ require_relative "unifont_glyphs"
5
7
 
6
8
  module TUITD
7
9
  class Screenshot
@@ -117,6 +119,10 @@ module TUITD
117
119
  "┃" => [true, true, false, false, :heavy],
118
120
  "║" => [true, true, false, false, :double],
119
121
  # corners
122
+ "╭" => [false, true, false, true, :light_rounded],
123
+ "╮" => [false, true, true, false, :light_rounded],
124
+ "╯" => [true, false, true, false, :light_rounded],
125
+ "╰" => [true, false, false, true, :light_rounded],
120
126
  "┌" => [false, true, false, true, :light],
121
127
  "┍" => [false, true, false, true, :light],
122
128
  "┎" => [false, true, false, true, :light],
@@ -126,7 +132,6 @@ module TUITD
126
132
  "┒" => [false, true, true, false, :light],
127
133
  "┓" => [false, true, true, false, :heavy],
128
134
  "└" => [true, false, false, true, :light],
129
- "▼" => [true, false, false, true, :light],
130
135
  "┖" => [true, false, false, true, :light],
131
136
  "┗" => [true, false, false, true, :heavy],
132
137
  "┘" => [true, false, true, false, :light],
@@ -220,6 +225,8 @@ module TUITD
220
225
  end
221
226
  end
222
227
 
228
+ draw_cursor(image)
229
+
223
230
  image.save(output_path)
224
231
  output_path
225
232
  end
@@ -248,12 +255,135 @@ module TUITD
248
255
  return
249
256
  end
250
257
 
251
- return if char == " " || char.ord < 32 || char.ord > 126
258
+ char_ord = char.ord
259
+ if char_ord == 10095 # '❯'
260
+ draw_chevron(image, px, py, fg_rgb)
261
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
262
+ return
263
+ elsif char_ord == 9210 || char_ord == 9679 # '⏺' or '●'
264
+ draw_circle(image, px, py, fg_rgb)
265
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
266
+ return
267
+ elsif char_ord >= 0x2800 && char_ord <= 0x28ff # Braille spinner
268
+ draw_braille(image, px, py, char, fg_rgb)
269
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
270
+ return
271
+ elsif char_ord == 0x2580 # '▀'
272
+ fill_rect(image, px, py, CELL_W, 8, fg_rgb)
273
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
274
+ return
275
+ elsif char_ord == 0x2584 # '▄'
276
+ fill_rect(image, px, py + 8, CELL_W, 8, fg_rgb)
277
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
278
+ return
279
+ elsif char_ord == 0x2588 # '█'
280
+ fill_rect(image, px, py, CELL_W, CELL_H, fg_rgb)
281
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
282
+ return
283
+ elsif char_ord == 0x25B2 # '▲'
284
+ draw_up_triangle(image, px, py, fg_rgb)
285
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
286
+ return
287
+ elsif char_ord == 0x25BC # '▼'
288
+ draw_down_triangle(image, px, py, fg_rgb)
289
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
290
+ return
291
+ elsif char_ord == 0x2713 # '✓'
292
+ draw_checkmark(image, px, py, fg_rgb)
293
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
294
+ return
295
+ elsif char_ord == 0x2717 # '✗'
296
+ draw_ballot_x(image, px, py, fg_rgb)
297
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
298
+ return
299
+ elsif char_ord == 0x2191 # '↑'
300
+ draw_up_arrow(image, px, py, fg_rgb)
301
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
302
+ return
303
+ elsif char_ord == 0x2192 # '→'
304
+ draw_right_arrow(image, px, py, fg_rgb)
305
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
306
+ return
307
+ elsif char_ord == 0x2193 # '↓'
308
+ draw_down_arrow(image, px, py, fg_rgb)
309
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
310
+ return
311
+ elsif char_ord == 0x2699 # '⚙'
312
+ draw_gear(image, px, py, fg_rgb)
313
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
314
+ return
315
+ elsif char_ord == 0x26A0 # '⚠'
316
+ draw_warning(image, px, py, fg_rgb)
317
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
318
+ return
319
+ elsif char_ord == 0x2026 # '…'
320
+ draw_ellipsis(image, px, py, fg_rgb)
321
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
322
+ return
323
+ elsif char_ord == 0x2014 # '—'
324
+ (px..(px + 7)).each { |x| image[x, py + 8] = ChunkyPNG::Color.rgb(*fg_rgb) }
325
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
326
+ return
327
+ elsif char_ord == 0x2190 # '←'
328
+ draw_left_arrow(image, px, py, fg_rgb)
329
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
330
+ return
331
+ elsif char_ord == 0x258C # '▌'
332
+ draw_left_half_block(image, px, py, fg_rgb)
333
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
334
+ return
335
+ elsif char_ord == 0x2590 # '▐'
336
+ draw_right_half_block(image, px, py, fg_rgb)
337
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
338
+ return
339
+ elsif char_ord == 0x2610 # '☐'
340
+ draw_empty_checkbox(image, px, py, fg_rgb)
341
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
342
+ return
343
+ elsif char_ord == 0x2611 # '☑'
344
+ draw_checked_checkbox(image, px, py, fg_rgb)
345
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
346
+ return
347
+ elsif char_ord == 0x2612 # '☒'
348
+ draw_x_checkbox(image, px, py, fg_rgb)
349
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
350
+ return
351
+ elsif char_ord == 0x2139 # 'ℹ'
352
+ draw_info_symbol(image, px, py, fg_rgb)
353
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
354
+ return
355
+ elsif char_ord == 0x2716 # '✖'
356
+ draw_heavy_x(image, px, py, fg_rgb)
357
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
358
+ return
359
+ end
360
+
361
+ return if char == " " || char_ord < 32
362
+
363
+ # ASCII printable (33-126): use Spleen bitmap font (pixel-perfect)
364
+ if char_ord <= 126
365
+ rows_data = glyph_rows(char)
366
+ if rows_data
367
+ draw_glyph(image, px, py, rows_data, fg_rgb, bold: bold, italic: italic)
368
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
369
+ return
370
+ end
371
+ end
252
372
 
253
- rows_data = glyph_rows(char)
254
- return unless rows_data
373
+ # Unicode (127+): try Unifont bitmap first (pixel-perfect like Spleen)
374
+ if char_ord > 126
375
+ unifont_rows = UnifontGlyphs.rows(char_ord)
376
+ if unifont_rows
377
+ draw_glyph(image, px, py, unifont_rows, fg_rgb, bold: bold, italic: italic)
378
+ draw_underline(image, px, py, CELL_W, fg_rgb) if underline
379
+ return
380
+ end
381
+ end
255
382
 
256
- draw_glyph(image, px, py, rows_data, fg_rgb, bold: bold, italic: italic)
383
+ # Fallback: render via Cairo for characters not in Unifont
384
+ if CairoRenderer.available?
385
+ CairoRenderer.render_glyph_onto(image, px, py, char, fg_rgb, bold: bold, italic: italic)
386
+ end
257
387
 
258
388
  draw_underline(image, px, py, CELL_W, fg_rgb) if underline
259
389
  end
@@ -331,7 +461,7 @@ module TUITD
331
461
  end
332
462
  if right
333
463
  ((cx - 2)..(px + 7)).each { |x| image[x, py + 6] = color }
334
- ((cx - 2)..(px + 7)).each { |x| image[x, py + 10] = color }
464
+ ((cx - 2)..(px + 10)).each { |x| image[x, py + 10] = color }
335
465
  end
336
466
  if up
337
467
  (py..(cy + 2)).each { |y| image[px + 2, y] = color }
@@ -370,6 +500,29 @@ module TUITD
370
500
  image[cx + 1, y] = color
371
501
  end
372
502
  end
503
+ elsif style == :light_rounded
504
+ case char
505
+ when "╭"
506
+ (px + 5..px + 7).each { |x| image[x, py + 8] = color }
507
+ (py + 10..py + 15).each { |y| image[px + 4, y] = color }
508
+ image[px + 4, py + 9] = color
509
+ image[px + 5, py + 9] = color
510
+ when "╮"
511
+ (px..px + 3).each { |x| image[x, py + 8] = color }
512
+ (py + 10..py + 15).each { |y| image[px + 4, y] = color }
513
+ image[px + 4, py + 9] = color
514
+ image[px + 3, py + 9] = color
515
+ when "╯"
516
+ (px..px + 3).each { |x| image[x, py + 8] = color }
517
+ (py..py + 6).each { |y| image[px + 4, y] = color }
518
+ image[px + 4, py + 7] = color
519
+ image[px + 3, py + 7] = color
520
+ when "╰"
521
+ (px + 5..px + 7).each { |x| image[x, py + 8] = color }
522
+ (py..py + 6).each { |y| image[px + 4, y] = color }
523
+ image[px + 4, py + 7] = color
524
+ image[px + 5, py + 7] = color
525
+ end
373
526
  else # :light
374
527
  if left
375
528
  (px..cx).each { |x| image[x, cy] = color }
@@ -386,5 +539,341 @@ module TUITD
386
539
  end
387
540
  end
388
541
 
542
+ def draw_cursor(image)
543
+ cursor_info = @state[:cursor] || @state["cursor"] || {}
544
+ cursor_vis = @state[:cursor_visible]
545
+ cursor_vis = cursor_info[:visible] if cursor_vis.nil?
546
+ cursor_vis = cursor_info["visible"] if cursor_vis.nil?
547
+ cursor_vis = false if cursor_vis.nil? # default invisible
548
+
549
+ return unless cursor_vis
550
+
551
+ ri = cursor_info[:row] || cursor_info["row"] || 0
552
+ ci = cursor_info[:col] || cursor_info["col"] || 0
553
+
554
+ return if ri < 0 || ri >= @rows || ci < 0 || ci >= @cols
555
+
556
+ style_val = @state[:cursor_style] || cursor_info[:style] || cursor_info["style"] || 1
557
+
558
+ px = ci * CELL_W
559
+ py = ri * CELL_H
560
+
561
+ color_rgb = [255, 255, 255] # Weiß standardmäßig
562
+ color = ChunkyPNG::Color.rgb(*color_rgb)
563
+
564
+ case style_val
565
+ when 1, 2 # Blinking Block oder Steady Block
566
+ CELL_H.times do |dy|
567
+ CELL_W.times do |dx|
568
+ x = px + dx
569
+ y = py + dy
570
+ next if x >= image.width || y >= image.height
571
+ original_color = image[x, y]
572
+ r = 255 - ChunkyPNG::Color.r(original_color)
573
+ g = 255 - ChunkyPNG::Color.g(original_color)
574
+ b = 255 - ChunkyPNG::Color.b(original_color)
575
+ image[x, y] = ChunkyPNG::Color.rgb(r, g, b)
576
+ end
577
+ end
578
+ when 3, 4 # Underline
579
+ 2.times do |h_offset|
580
+ y = py + CELL_H - 1 - h_offset
581
+ next if y >= image.height
582
+ CELL_W.times do |dx|
583
+ x = px + dx
584
+ next if x >= image.width
585
+ image[x, y] = color
586
+ end
587
+ end
588
+ when 5, 6 # Bar
589
+ 2.times do |w_offset|
590
+ x = px + w_offset
591
+ next if x >= image.width
592
+ CELL_H.times do |dy|
593
+ y = py + dy
594
+ next if y >= image.height
595
+ image[x, y] = color
596
+ end
597
+ end
598
+ end
599
+ end
600
+
601
+ def draw_chevron(image, px, py, fg_rgb)
602
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
603
+ (0..3).each do |i|
604
+ image[px + 2 + i, py + 4 + i] = color
605
+ image[px + 3 + i, py + 4 + i] = color # bold/thick chevron
606
+
607
+ image[px + 5 - i, py + 8 + i] = color
608
+ image[px + 6 - i, py + 8 + i] = color # bold/thick chevron
609
+ end
610
+ end
611
+
612
+ def draw_circle(image, px, py, fg_rgb)
613
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
614
+ cx = px + 4
615
+ cy = py + 8
616
+ (-3..3).each do |dy|
617
+ r_width = case dy.abs
618
+ when 3 then 1
619
+ when 2 then 2
620
+ else 3
621
+ end
622
+ (-r_width..r_width).each do |dx|
623
+ x = cx + dx
624
+ y = cy + dy
625
+ next if x < px || x >= px + CELL_W || y < py || y >= py + CELL_H
626
+ image[x, y] = color
627
+ end
628
+ end
629
+ end
630
+
631
+ def draw_braille(image, px, py, char, fg_rgb)
632
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
633
+ mask = char.ord - 0x2800
634
+ dot_coords = [
635
+ [2, 3], # Dot 1
636
+ [2, 6], # Dot 2
637
+ [2, 9], # Dot 3
638
+ [5, 3], # Dot 4
639
+ [5, 6], # Dot 5
640
+ [5, 9], # Dot 6
641
+ [2, 12], # Dot 7
642
+ [5, 12] # Dot 8
643
+ ]
644
+ dot_coords.each_with_index do |(dx, dy), idx|
645
+ if (mask & (1 << idx)) != 0
646
+ 2.times do |ddy|
647
+ 2.times do |ddx|
648
+ x = px + dx + ddx
649
+ y = py + dy + ddy
650
+ next if x >= image.width || y >= image.height
651
+ image[x, y] = color
652
+ end
653
+ end
654
+ end
655
+ end
656
+ end
657
+
658
+ def draw_up_triangle(image, px, py, fg_rgb)
659
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
660
+ (5..8).each do |dy|
661
+ width = dy - 5
662
+ (4 - width..4 + width).each do |dx|
663
+ image[px + dx, py + dy] = color
664
+ end
665
+ end
666
+ end
667
+
668
+ def draw_down_triangle(image, px, py, fg_rgb)
669
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
670
+ (5..8).each do |dy|
671
+ width = 8 - dy
672
+ (4 - width..4 + width).each do |dx|
673
+ image[px + dx, py + dy] = color
674
+ end
675
+ end
676
+ end
677
+
678
+ def draw_checkmark(image, px, py, fg_rgb)
679
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
680
+ image[px + 2, py + 8] = color
681
+ image[px + 3, py + 9] = color
682
+ image[px + 4, py + 10] = color
683
+ image[px + 5, py + 8] = color
684
+ image[px + 6, py + 6] = color
685
+ image[px + 7, py + 4] = color
686
+ end
687
+
688
+ def draw_ballot_x(image, px, py, fg_rgb)
689
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
690
+ image[px + 2, py + 5] = color
691
+ image[px + 3, py + 6] = color
692
+ image[px + 3, py + 7] = color
693
+ image[px + 4, py + 8] = color
694
+ image[px + 5, py + 9] = color
695
+ image[px + 5, py + 10] = color
696
+ image[px + 6, py + 11] = color
697
+ image[px + 2, py + 11] = color
698
+ image[px + 3, py + 10] = color
699
+ image[px + 3, py + 9] = color
700
+ image[px + 5, py + 7] = color
701
+ image[px + 5, py + 6] = color
702
+ image[px + 6, py + 5] = color
703
+ end
704
+
705
+ def draw_up_arrow(image, px, py, fg_rgb)
706
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
707
+ (3..12).each { |dy| image[px + 4, py + dy] = color }
708
+ image[px + 3, py + 4] = color
709
+ image[px + 5, py + 4] = color
710
+ image[px + 2, py + 5] = color
711
+ image[px + 6, py + 5] = color
712
+ end
713
+
714
+ def draw_down_arrow(image, px, py, fg_rgb)
715
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
716
+ (3..12).each { |dy| image[px + 4, py + dy] = color }
717
+ image[px + 3, py + 11] = color
718
+ image[px + 5, py + 11] = color
719
+ image[px + 2, py + 10] = color
720
+ image[px + 6, py + 10] = color
721
+ end
722
+
723
+ def draw_right_arrow(image, px, py, fg_rgb)
724
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
725
+ (1..6).each { |dx| image[px + dx, py + 8] = color }
726
+ image[px + 5, py + 7] = color
727
+ image[px + 5, py + 9] = color
728
+ image[px + 4, py + 6] = color
729
+ image[px + 4, py + 10] = color
730
+ end
731
+
732
+ def draw_gear(image, px, py, fg_rgb)
733
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
734
+ image[px + 4, py + 6] = color
735
+ image[px + 4, py + 10] = color
736
+ image[px + 2, py + 8] = color
737
+ image[px + 6, py + 8] = color
738
+ image[px + 3, py + 7] = color
739
+ image[px + 5, py + 7] = color
740
+ image[px + 3, py + 9] = color
741
+ image[px + 5, py + 9] = color
742
+ image[px + 4, py + 5] = color
743
+ image[px + 4, py + 11] = color
744
+ image[px + 1, py + 8] = color
745
+ image[px + 7, py + 8] = color
746
+ image[px + 2, py + 6] = color
747
+ image[px + 6, py + 6] = color
748
+ image[px + 2, py + 10] = color
749
+ image[px + 6, py + 10] = color
750
+ end
751
+
752
+ def draw_warning(image, px, py, fg_rgb)
753
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
754
+ image[px + 4, py + 3] = color
755
+ image[px + 3, py + 4] = color
756
+ image[px + 5, py + 4] = color
757
+ image[px + 3, py + 5] = color
758
+ image[px + 5, py + 5] = color
759
+ image[px + 2, py + 6] = color
760
+ image[px + 6, py + 6] = color
761
+ image[px + 2, py + 7] = color
762
+ image[px + 6, py + 7] = color
763
+ image[px + 1, py + 8] = color
764
+ image[px + 7, py + 8] = color
765
+ image[px + 1, py + 9] = color
766
+ image[px + 7, py + 9] = color
767
+ (1..7).each { |dx| image[px + dx, py + 10] = color }
768
+ image[px + 4, py + 5] = color
769
+ image[px + 4, py + 6] = color
770
+ image[px + 4, py + 7] = color
771
+ image[px + 4, py + 9] = color
772
+ end
773
+
774
+ def draw_ellipsis(image, px, py, fg_rgb)
775
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
776
+ image[px + 1, py + 12] = color
777
+ image[px + 4, py + 12] = color
778
+ image[px + 6, py + 12] = color
779
+ end
780
+
781
+ def draw_left_arrow(image, px, py, fg_rgb)
782
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
783
+ (1..6).each { |dx| image[px + dx, py + 8] = color }
784
+ image[px + 2, py + 7] = color
785
+ image[px + 2, py + 9] = color
786
+ image[px + 3, py + 6] = color
787
+ image[px + 3, py + 10] = color
788
+ end
789
+
790
+ def draw_left_half_block(image, px, py, fg_rgb)
791
+ fill_rect(image, px, py, 4, CELL_H, fg_rgb)
792
+ end
793
+
794
+ def draw_right_half_block(image, px, py, fg_rgb)
795
+ fill_rect(image, px + 4, py, 4, CELL_H, fg_rgb)
796
+ end
797
+
798
+ def draw_checkbox_border(image, px, py, color)
799
+ (1..6).each do |dx|
800
+ image[px + dx, py + 4] = color
801
+ image[px + dx, py + 11] = color
802
+ end
803
+ (4..11).each do |dy|
804
+ image[px + 1, py + dy] = color
805
+ image[px + 6, py + dy] = color
806
+ end
807
+ end
808
+
809
+ def draw_empty_checkbox(image, px, py, fg_rgb)
810
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
811
+ draw_checkbox_border(image, px, py, color)
812
+ end
813
+
814
+ def draw_checked_checkbox(image, px, py, fg_rgb)
815
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
816
+ draw_checkbox_border(image, px, py, color)
817
+ image[px + 2, py + 8] = color
818
+ image[px + 3, py + 9] = color
819
+ image[px + 4, py + 7] = color
820
+ image[px + 5, py + 5] = color
821
+ end
822
+
823
+ def draw_x_checkbox(image, px, py, fg_rgb)
824
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
825
+ draw_checkbox_border(image, px, py, color)
826
+ image[px + 2, py + 5] = color
827
+ image[px + 2, py + 6] = color
828
+ image[px + 3, py + 7] = color
829
+ image[px + 3, py + 8] = color
830
+ image[px + 4, py + 7] = color
831
+ image[px + 4, py + 8] = color
832
+ image[px + 5, py + 9] = color
833
+ image[px + 5, py + 10] = color
834
+ image[px + 2, py + 10] = color
835
+ image[px + 2, py + 9] = color
836
+ image[px + 5, py + 6] = color
837
+ image[px + 5, py + 5] = color
838
+ end
839
+
840
+ def draw_info_symbol(image, px, py, fg_rgb)
841
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
842
+ (3..5).each do |dx|
843
+ image[px + dx, py + 4] = color
844
+ image[px + dx, py + 12] = color
845
+ end
846
+ image[px + 2, py + 5] = color
847
+ image[px + 6, py + 5] = color
848
+ image[px + 2, py + 11] = color
849
+ image[px + 6, py + 11] = color
850
+ (6..10).each do |dy|
851
+ image[px + 1, py + dy] = color
852
+ image[px + 7, py + dy] = color
853
+ end
854
+ image[px + 4, py + 6] = color
855
+ (8..10).each do |dy|
856
+ image[px + 4, py + dy] = color
857
+ end
858
+ end
859
+
860
+ def draw_heavy_x(image, px, py, fg_rgb)
861
+ color = ChunkyPNG::Color.rgb(*fg_rgb)
862
+ [4, 5, 11, 12].each do |dy|
863
+ image[px + 1, py + dy] = color
864
+ image[px + 2, py + dy] = color
865
+ image[px + 5, py + dy] = color
866
+ image[px + 6, py + dy] = color
867
+ end
868
+ [6, 7, 9, 10].each do |dy|
869
+ image[px + 2, py + dy] = color
870
+ image[px + 3, py + dy] = color
871
+ image[px + 4, py + dy] = color
872
+ image[px + 5, py + dy] = color
873
+ end
874
+ image[px + 3, py + 8] = color
875
+ image[px + 4, py + 8] = color
876
+ end
877
+
389
878
  end
390
879
  end