Stawlie0

Untitled

Oct 7th, 2023
711
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C++ 11.55 KB | None | 0 0
  1. --SWITCH
  2. local MODE = nil
  3. local PALETTE_PATH = "palette.cia"
  4.  
  5. --MODES
  6. local MODES = {
  7.     ["MODE_24BIT"] = "24bit",
  8. }
  9.  
  10. --CONSTANTS--
  11. local FOREGROUND = 3
  12. local BACKGROUND = 2
  13. local ALPHA_CHANNEL = 1
  14.  
  15. local UTF8_CHAR = 0
  16. local COMPRESSED_PIXEL = 1
  17.  
  18. local ELEMENT_COUNT = 2
  19. local ELEMENT_COUNT_FULL_ARRAY = 4
  20.  
  21. local IMAGE_WIDTH  = 1
  22. local IMAGE_HEIGHT = 2
  23. local IMAGE_FRAMES = 3
  24. local IMAGE        = 4
  25.  
  26. local BYTE_SIZE = 8
  27. local NULL_CHAR = 0
  28.  
  29. local FILE_OPEN_ERROR = "Can't open file"
  30. local LOAD_PALETTE_ERROR = "Can't load color index array. Table = nil or Table size less 256[0-255]"
  31. local SIGNATURE_MATCH_ERROR = "Signatures not match"
  32.  
  33. --Загрузка палитры
  34. local palette = nil
  35. function LOAD_PALETTE( color_index_array )
  36.     palette = color_index_array
  37. end
  38.  
  39. local function reloadPalette()
  40.     dofile( PALETTE_PATH )
  41.     if ( not palette or #palette ~= 255 ) then
  42.         error( LOAD_PALETTE_ERROR )
  43.     end
  44. end
  45.  
  46. --.ocif подпись
  47. local ocif_signature1 = 0x896F6369
  48. local ocif_signature2 = 0x00661A0A --7 bytes: 89 6F 63 69 66 1A 0A
  49. local ocif_signature_expand = { string.char(0x89), string.char(0x6F), string.char(0x63), string.char(0x69), string.char(0x66), string.char(0x1A), string.char(0x0A) }
  50.  
  51. --Выделить бит-терминатор в первом байте utf8 символа: 1100 0010 --> 0010 0000
  52. local function selectTerminateBit_l()
  53.     local prevByte = nil
  54.     local prevTerminateBit = nil
  55.  
  56.     return function( byte )
  57.         local x, terminateBit = nil
  58.         if ( prevByte == byte ) then
  59.             return prevTerminateBit
  60.         end
  61.  
  62.         x = bit32.band( bit32.bnot(byte), 0x000000FF )
  63.         x = bit32.bor( x, bit32.rshift(x, 1) )
  64.         x = bit32.bor( x, bit32.rshift(x, 2) )
  65.         x = bit32.bor( x, bit32.rshift(x, 4) )
  66.         x = bit32.bor( x, bit32.rshift(x, 8) )
  67.         x = bit32.bor( x, bit32.rshift(x, 16) )
  68.  
  69.         terminateBit = x - bit32.rshift(x, 1)
  70.  
  71.         --save state
  72.         prevByte = byte
  73.         prevTerminateBit = terminateBit
  74.  
  75.         return terminateBit
  76.     end
  77. end
  78. local selectTerminateBit = selectTerminateBit_l()
  79.  
  80. --Прочитать n <= 4 байтов из файла, возвращает прочитанные байты как число, если не удалось прочитать, то возвращает 0
  81. local function readBytes(file, bytes)
  82.   local readedByte = 0
  83.   local readedNumber = 0
  84.   for i = bytes, 1, -1 do
  85.     readedByte = string.byte( file:read(1) or NULL_CHAR )
  86.     readedNumber = readedNumber + bit32.lshift(readedByte, i*8-8)
  87.   end
  88.  
  89.   return readedNumber
  90. end
  91.  
  92. --Преобразует цвет в hex записи в rgb запись
  93. local function HEXtoRGB(color)
  94.   local rr = bit32.extract( color, 16, 8 )
  95.   local gg = bit32.extract( color, 8,  8 )
  96.   local bb = bit32.extract( color, 0,  8 )
  97.  
  98.   return rr, gg, bb
  99. end
  100.  
  101. local function RGBtoHEX(rr, gg, bb)
  102.   return bit32.lshift(rr, 16) + bit32.lshift(gg, 8) + bb
  103. end
  104.  
  105. --Альфа-смешение
  106. local function alphaBlend(background_screen_color, foreground_image_color, alpha_channel)
  107.     if ( alpha_channel <= 0 ) then return foreground_image_color end
  108.  
  109.     local INVERTED_ALPHA_CHANNEL = 255-alpha_channel
  110.  
  111.     local background_screen_color_rr, background_screen_color_gg, background_screen_color_bb    = HEXtoRGB(background_screen_color)
  112.     local foreground_image_color_rr, foreground_image_color_gg, foreground_image_color_bb = HEXtoRGB(foreground_image_color)
  113.  
  114.     local blended_rr = foreground_image_color_rr * INVERTED_ALPHA_CHANNEL / 255 + background_screen_color_rr * alpha_channel / 255
  115.     local blended_gg = foreground_image_color_gg * INVERTED_ALPHA_CHANNEL / 255 + background_screen_color_gg * alpha_channel / 255
  116.     local blended_bb = foreground_image_color_bb * INVERTED_ALPHA_CHANNEL / 255 + background_screen_color_bb * alpha_channel / 255
  117.  
  118.     return RGBtoHEX( blended_rr, blended_gg, blended_bb )
  119. end
  120.  
  121. --Конвертация 24 битной палитры в 8 битную
  122. local function HEX_color24to8_l()
  123.     local prevHexcolor24 = nil
  124.     local prevIndex = 0
  125.  
  126.     return function( hexcolor24 )
  127.         if ( hexcolor24 == prevHexcolor24 ) then
  128.             return prevIndex
  129.         end
  130.         prevHexcolor24 = hexcolor24
  131.  
  132.         local rr, gg, bb = HEXtoRGB( hexcolor24 )
  133.         local reducedColor = math.modf(rr / 0x33) * 0x330000 + math.modf(gg / 0x24) * 0x2400 + math.modf(bb / 0x3F) * 0x3F
  134.  
  135.         for color_index, color in pairs(palette) do
  136.             if ( hexcolor24 == color or reducedColor == color ) then
  137.                 prevIndex = color_index
  138.                 return color_index
  139.             end
  140.         end
  141.  
  142.         return 0
  143.     end
  144. end
  145. local HEX_color24to8 = HEX_color24to8_l()
  146.  
  147. --Конвертация 8 битной палитры в 24 битную
  148. local function HEX_color8to24( hexcolor8 )
  149.     return palette[hexcolor8]
  150. end
  151.  
  152. --Сжатие пикселя, исключая символ
  153. local function compressPixel(foreground, background, alpha)
  154.     return bit32.lshift( foreground, BYTE_SIZE*2 ) + bit32.lshift( background, BYTE_SIZE ) + alpha
  155. end
  156.  
  157. --Восстановление пикселя
  158. local function decompressPixel( compressed_pixel )
  159.     return bit32.rshift( compressed_pixel, BYTE_SIZE*2 ), bit32.rshift( bit32.band( compressed_pixel, 0x00FF00 ), BYTE_SIZE ), bit32.band( compressed_pixel, 0x0000FF )
  160. end
  161.  
  162. --Подготавливает цвета и символ для записи в файл
  163. local function encodePixel(compressed_pixel, _, _, _, char)
  164.     local decompressed_fg, decompressed_bg, alpha = decompressPixel( compressed_pixel )
  165.     local ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 = string.byte( char, 1, 6 )
  166.  
  167.     ascii_char1 = ascii_char1 or NULL_CHAR
  168.  
  169.     return { decompressed_fg, decompressed_bg, alpha, ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 }
  170. end
  171.  
  172. local function encodePixel_full_array(_, hexcolor_fg, hexcolor_bg, alpha, char)
  173.     local converted_fg = HEX_color24to8( hexcolor_fg )
  174.     local converted_bg = HEX_color24to8( hexcolor_bg )
  175.     local ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 = string.byte( char, 1, 6 )
  176.  
  177.     ascii_char1 = ascii_char1 or NULL_CHAR
  178.  
  179.     return { converted_fg, converted_bg, alpha, ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 }
  180. end
  181.  
  182. local function encodePixel_24bit_mode(_, hexcolor_fg, hexcolor_bg, alpha, char)
  183.     local red_fg, green_fg, blue_fg = HEXtoRGB( hexcolor_fg )
  184.     local red_bg, green_bg, blue_bg = HEXtoRGB( hexcolor_bg )
  185.     local ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 = string.byte( char, 1, 6 )
  186.     ascii_char1 = ascii_char1 or NULL_CHAR
  187.  
  188.     return { red_fg, green_fg, blue_fg, red_bg, green_bg, blue_bg, alpha, ascii_char1, ascii_char2, ascii_char3, ascii_char4, ascii_char5, ascii_char6 }
  189. end
  190.  
  191. --Выбор метода записи
  192. local encodePixel =
  193. {
  194.     [MODES.MODE_24BIT] = encodePixel_24bit_mode,
  195.  
  196.     [true]  = encodePixel_full_array,
  197.     [false] = encodePixel
  198. }
  199.  
  200. --Декодирование utf8 символа
  201. local function decodeChar(file)
  202.     local firstUTF8Byte = readBytes(file, 1)
  203.     local charcodeArray = {firstUTF8Byte}
  204.     local utf8Length = 1
  205.  
  206.     local terminateBit = selectTerminateBit(firstUTF8Byte)
  207.     if ( terminateBit == 32 ) then
  208.         utf8Length = 2
  209.     elseif ( terminateBit == 16 ) then
  210.         utf8Length = 3
  211.     elseif ( terminateBit == 8 ) then
  212.         utf8Length = 4
  213.     elseif ( terminateBit == 4 ) then
  214.         utf8Length = 5
  215.     elseif ( terminateBit == 2 ) then
  216.         utf8Length = 6
  217.     end
  218.  
  219.     for i = 1, utf8Length-1 do
  220.         table.insert( charcodeArray, readBytes(file, 1) )
  221.     end
  222.  
  223.     return string.char( table.unpack( charcodeArray ) )
  224. end
  225.  
  226. local imageAPI = {}
  227.  
  228. --Установить режим работы
  229. function imageAPI.setMode( mode )
  230.     if ( mode == "8bit" ) then
  231.         MODE = nil
  232.     else
  233.         MODE = mode
  234.     end
  235. end
  236.  
  237. --Установить путь до палитры
  238. function imageAPI.setPalette( palette_path )
  239.     PALETTE_PATH = palette_path or PALETTE_PATH
  240.     reloadPalette()
  241. end
  242.  
  243. --Получить текущий режим
  244. function imageAPI.getMode()
  245.     return MODE
  246. end
  247.  
  248. --Запись в файл по массиву изображения
  249. function imageAPI.write(path, picture, full_array)
  250.     local elementCount = (full_array or MODE == MODES.MODE_24BIT) and ELEMENT_COUNT_FULL_ARRAY or ELEMENT_COUNT
  251.     local encodedPixel = nil
  252.  
  253.     local file = assert( io.open(path, "w"), FILE_OPEN_ERROR )
  254.  
  255.     file:write( table.unpack(ocif_signature_expand) )
  256.     file:write( string.char( picture[IMAGE_WIDTH]  ) )
  257.     file:write( string.char( picture[IMAGE_HEIGHT] ) )
  258.     file:write( string.char( picture[IMAGE_FRAMES] ) )
  259.    
  260.     for frame=0, picture[IMAGE_FRAMES]-1, 1 do
  261.         for element = elementCount, picture[IMAGE_HEIGHT] * picture[IMAGE_WIDTH] * elementCount, elementCount do
  262.             --Подготовка пикселя к записи
  263.             encodedPixel = encodePixel[MODE or full_array or false]
  264.             (
  265.                 picture[IMAGE+frame][element-COMPRESSED_PIXEL],
  266.                 picture[IMAGE+frame][element-FOREGROUND],
  267.                 picture[IMAGE+frame][element-BACKGROUND],
  268.                 picture[IMAGE+frame][element-ALPHA_CHANNEL],
  269.                 picture[IMAGE+frame][element-UTF8_CHAR]
  270.             )
  271.  
  272.             --Запись
  273.             for i = 1, #encodedPixel do
  274.                 file:write( string.char( encodedPixel[i] ) )
  275.             end
  276.         end
  277.     end
  278.  
  279.     file:close()
  280. end
  281.  
  282. --Чтение из файла, возвращет массив изображения
  283. function imageAPI.read(path, full_array)
  284.     local picture = {}
  285.     local elementCount = (MODE == MODES.MODE_24BIT) and ELEMENT_COUNT_FULL_ARRAY or ELEMENT_COUNT
  286.     local file = assert( io.open(path, "rb"), FILE_OPEN_ERROR )
  287.  
  288.     --Чтение подписи файла
  289.     local signature1, signature2 = readBytes(file, 4), readBytes(file, 3)
  290.     if ( signature1 ~= ocif_signature1 or signature2 ~= ocif_signature2 ) then
  291.         file:close()
  292.         error( SIGNATURE_MATCH_ERROR )
  293.     end
  294.  
  295.     picture[IMAGE_WIDTH]  = readBytes(file, 1)
  296.     picture[IMAGE_HEIGHT] = readBytes(file, 1)
  297.     picture[IMAGE_FRAMES] = readBytes(file, 1)
  298.  
  299.     for frame=0, picture[IMAGE_FRAMES]-1, 1 do
  300.         picture[IMAGE+frame] = {}
  301.  
  302.         for element = elementCount, picture[IMAGE_HEIGHT] * picture[IMAGE_WIDTH] * elementCount, elementCount do
  303.             if ( MODE == MODES.MODE_24BIT or full_array ) then
  304.                 picture[IMAGE+frame][element-FOREGROUND] = readBytes(file, 3)
  305.                 picture[IMAGE+frame][element-BACKGROUND] = readBytes(file, 3)
  306.                 picture[IMAGE+frame][element-ALPHA_CHANNEL] = readBytes(file, 1)
  307.             else
  308.                 picture[IMAGE+frame][element-COMPRESSED_PIXEL] = readBytes(file, 3)
  309.             end
  310.  
  311.             picture[IMAGE+frame][element-UTF8_CHAR] = decodeChar( file )
  312.         end
  313.     end
  314.  
  315.     file:close()
  316.  
  317.     return picture
  318. end
  319.  
  320. --Отрисовка изображения(Для примера. Медленная. Делайте СВОЮ отрисовку)
  321. function imageAPI.draw(picture, frame, sx, sy, gpu)
  322.     frame = frame - 1
  323.     local x, y = 0, 0
  324.  
  325.     local screenBg = nil
  326.     local fg8bit, bg8bit, alpha, fg24bit, bg24bit = nil
  327.  
  328.     local elementCount = (MODE == MODES.MODE_24BIT) and ELEMENT_COUNT_FULL_ARRAY or ELEMENT_COUNT
  329.     for element = elementCount, picture[IMAGE_HEIGHT] * picture[IMAGE_WIDTH] * elementCount, elementCount do   
  330.         x = (x % picture[IMAGE_WIDTH]) + 1
  331.         y = (x == 1) and y+1 or y
  332.  
  333.         _, _, screenBg = gpu.get(sx+x-1, sy+y-1)
  334.  
  335.         if ( MODE == MODES.MODE_24BIT ) then
  336.             fg24bit, bg24bit, alpha = picture[IMAGE+frame][element-FOREGROUND], picture[IMAGE+frame][element-BACKGROUND], picture[IMAGE+frame][element-ALPHA_CHANNEL]
  337.         else
  338.             fg8bit, bg8bit, alpha = decompressPixel( picture[IMAGE+frame][element-COMPRESSED_PIXEL] )
  339.             fg24bit, bg24bit = HEX_color8to24( fg8bit ), HEX_color8to24( bg8bit )
  340.         end
  341.  
  342.         gpu.setForeground( fg24bit )
  343.         gpu.setBackground( alphaBlend( screenBg, bg24bit, alpha ) )
  344.  
  345.         gpu.set(sx+x-1, sy+y-1, picture[IMAGE+frame][element-UTF8_CHAR])
  346.     end
  347. end
  348.  
  349. return imageAPI
Advertisement
Add Comment
Please, Sign In to add comment