Перейти к содержимому

some blog name

  • записей
    15
  • комментарий
    41
  • просмотра
    17 744

Файловый менеджер. Часть #1 [иконки]

Doob

1 295 просмотров

Чтобы было куда кликать, надо на экране разметить места для иконок, еще и иконки нарисовать.

Для иконок возьмем формат PPM, а конкретно, цветную бинарную версию P6. Формат ультра-примитивный, иконки можно будет без лишних заморочек рисовать в любом нормальном растровом редакторе, в опенкомпах, а при наличии нужного скрипта - прямо в консоли.

Но в этом формате будем хранить иконки на диске. Внутри программы они будут преобразовываться в таблицу, хранящую цвет каждого пикселя.

 

Например, создадим иконку, которая будет рисоваться для всех типов файлов по умолчанию. (Будет отображаться, если подходящая иконка не загружена)

local icons = {} -- создать массив с иконками
local icons.unknown = {x = 10} -- создать таблицу для иконки, указать ширину в пикселях
for i = 1, 100 do -- цикл заполнения таблицы 10x10
  if i%3 == 0 then -- если номер пикселя делится на 3
    icons.unknown[i] = 3394611 -- сделать пиксель зеленым
  else -- иначе
    icons.unknown[i] = 3355443 -- сделать серым
  end
end

 

С внутренним представлением определились, теперь напишем функцию отрисовки.

Создадим счетчики для индексов, горизонтальной и вертикальной координаты.

Запустим цикл, с условием: пока индекс меньше или равен (количество пикселей - ширина изображения).

Установим для символа цвет текущего пикселя, а для фона получим пиксель через текущий индекс + ширина изображения.

И выведем полученные пиксели одним символом u+2580.

Если счетчик по горизонтали досчитал до ширины изображения - сбросить в начало, к счетчику по вертикали добавить 1, а к индексу прибавить ширину. Получим пропуск строки, т. к. она уже была отрисована в текущей итерации.

 

local function draw_icon(name, X, Y) -- получить название и координаты
  if not icons[name] then return false end -- прервать, если нет такой иконки
  local x, y, index = 1, 1, 1 -- создать счетчики
  while index <= #icons[name]-icons[name].x do -- пройти по индексам
    gpu.setForeground(icons[name][index]) -- установить цвет верхнего пикселя
    gpu.setBackground(icons[name][index+icons[name].x]) -- цвет нижнего
    gpu.set(x+X-1, y+Y-1, quad) -- вывести на экран
    if x == icons[name].x then -- если достигнута ширина изображения
      x, y, index = 1, y + 1, index + icons[name].x+1 -- обновить все счетчики
    else -- простая итерация
      x, index = x + 1, index + 1 -- обновить счетчик горизонтали и индекса
    end
  end
end

Одна иконка уже сгенерирована, чтобы вывести ее в углу экрана, вызовем ее по имени - draw_icon('unknown', 1, 1)

В итоге, на экране получим такое изображение:

UOqfpOl.png

 

Чтобы загрузить иконки и конвертировать в удобный вид, создадим такую функцию:

Перебрать все файлы в папке, получив их список через filesystem API.

Прочитать файл построчно в таблицу, попутно удалив строки с комментариями.

Если заголовок файла равен , получить ширину картинки, информацию о пикселях объединить в одну строку.

В цикле пройти по пикселям, конвертируя бинарное значение пикселя в число.

Тут надо помнить, что значение одного пикселя хранится в трех символах (по одному на канал), поэтому цикл будет скакать через 3. Первый символ конвертируем в число, умножаем на 65536, второй на 256 и складываем. Полученное число добавляем в массив пикселей текущей иконки.

 

Получаем примерно такую реализацию:

local function load_icons(path) -- получить путь к папке с иконками
  local multiplier, path = {65536, 256, 1}, path or '' -- создать таблицу множителей
  for name in fs.list(path) do -- получить имя файла в папке
    local file = io.open(path..name, 'r') -- открыть файл
    if not file then break end -- если файла нет, прервать цикл
    name = name:gsub('%..+', '') -- обрезать название файла до первой точки
    local raw_img = {} -- создать массив для сырых данных
    for line in file:lines() do -- в цикле пройти по строкам
      if line and line:sub(1,1) ~= '#' then -- если строка не закоментированна
        table.insert(raw_img, line) -- добавить в таблицу
      end
    end
    file:close() -- закрыть файл
    if raw_img[1] == 'P6' then -- если заголовок совпадает
      local _ = raw_img[2]:find(' ') -- проверить наличие пробела на второй строке
      if _ then -- если размеры на одной строке
        _, raw_img[2] = raw_img[2]:sub(_+1), raw_img[2]:sub(1,_-1) -- разделить
        table.insert(raw_img, 3, _) -- перенести высоту на другую строку
      end
      raw_img[2] = tonumber(raw_img[2]) -- преобразовать ширину изображения в число
      icons[name] = {x = raw_img[2]} -- создать пустую таблицу для пикселей
      local current = '' -- создать переменную с сырой информацией о пикселях
      for i = 5, #raw_img do -- пройти до конца файла
        current = current..raw_img[i]..'\n' -- объединить данные в одну строку
      end
      local color, n
      for i = 1, #current-1, 3 do -- пройти по каждому третьему символу, исключая последний перевод строки
        n, color = 1, 0 -- сбросить счетчик для таблицы множителей и цвет
        for j = i, i+2 do -- перебрать три символа
          color = color+current:sub(j,j):byte()*multiplier[n] -- преобразовать символ в число и добавить к значению цвета
          n = n + 1 -- обновить счетчик
        end
        table.insert(icons[name], color) -- добавить цвет пикселя к остальным
      end
    end
  end
end

 

Реализация очень примитивная, но главное, что иконки будут загружаться. Можно было бы сотворить свой формат, который быстрей распаковывается и занимает меньше места, но плодить сущностей очень вредно.

 

Для проверки, осталось нарисовать иконки, сложить их в папку /home/icons/, например. И запустить весь код:

local fs = require('filesystem')
local gpu = require('component').gpu
local quad = require('unicode').char(0x2580)
local icons = {unknown = {x = 10}}
for i = 1, 100 do
  if i%3 == 0 then
    icons.unknown[i] = 3394611
  else
    icons.unknown[i] = 3355443
  end
end

local function load_icons(path)
  local multiplier, path = {65536, 256, 1}, path or ''
  for name in fs.list(path) do
    local file = io.open(path..name, 'r')
    if not file then break end
    name = name:gsub('%..+', '')
    local raw_img = {}
    for line in file:lines() do
      if line and line:sub(1,1) ~= '#' then
        table.insert(raw_img, line)
      end
    end
    file:close()
    if raw_img[1] == 'P6' then
      local _ = raw_img[2]:find(' ')
      if _ then
        _, raw_img[2] = raw_img[2]:sub(_+1), raw_img[2]:sub(1,_-1)
        table.insert(raw_img, 3, _)
      end
      raw_img[2] = tonumber(raw_img[2])
      icons[name] = {x = raw_img[2]}
      local current = ''
      for i = 5, #raw_img do
        current = current..raw_img[i]..'\n'
      end
      local n, color
      for i = 1, #current-1, 3 do
        n, color = 1, 0
        for j = i, i+2 do
          color = color+current:sub(j,j):byte()*multiplier[n]
          n = n+1
        end
        table.insert(icons[name], color)
      end
    end
  end
end

local function draw_icon(name, X, Y)
  if not icons[name] then return false end
  local x, y, index = 1, 1, 1
  while index <= #icons[name]-icons[name].x do
    gpu.setForeground(icons[name][index])
    gpu.setBackground(icons[name][index+icons[name].x])
    gpu.set(x+X-1, y+Y-1, quad)
    if x == icons[name].x then
      x, y, index = 1, y + 1, index + icons[name].x+1
    else
      x, index = x + 1, index + 1
    end
  end
end

load_icons('/home/icons/')
local n = 1
for name in pairs(icons) do
  draw_icon(name, (n*11)-10, 1)
  n = n + 1
end

 

Получаем:

VcgMETI.png

  • Нравится 3


0 комментариев


Рекомендуемые комментарии

Нет комментариев для отображения

Гость
Добавить комментарий...

×   Вы вставили отформатированное содержимое.   Удалить форматирование

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отобразить как ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...