48 Commits

Author SHA1 Message Date
Ishotihadus 823ef428c1 version 3.9.6 2018-07-16 02:30:44 +09:00
Ishotihadus 67b4d1a2e7 add usamin support 2018-07-16 02:29:28 +09:00
Ishotihadus f13188f812 add resS support 2018-07-16 02:26:23 +09:00
Ishotihadus 64d308402d improve stability of mikunyan-image 2018-07-15 00:17:51 +09:00
Ishotihadus 6b477a6b92 fix etc2rgba8 decoding bug 2018-05-27 23:26:54 +09:00
Ishotihadus 574a4f5b34 version 3.9.5 2018-05-27 22:25:25 +09:00
Ishotihadus 6313515291 add documentation 2018-05-27 22:23:41 +09:00
Ishotihadus c3abc515dc Fix ETC2 decoding bug 2018-05-27 22:16:46 +09:00
Ishotihadus d9fb1db793 Add some TypeTrees 2018-05-27 21:59:51 +09:00
Ishotihadus c19aec52a3 improve platform compatibility 2018-05-27 18:32:41 +09:00
Ishotihadus cc383eb5de minor fix 2018-05-27 18:03:29 +09:00
Ishotihadus e9d27cb7cc improve stability of mikunyan-image 2018-05-27 17:25:01 +09:00
Ishotihadus 6a0cc560f5 ETC Native Decoding Support 2018-05-27 17:17:37 +09:00
Ishotihadus ac7b0a2806 refactor rgb565 2018-05-27 04:31:10 +09:00
Ishotihadus 71dd94b76f refactoring 2018-05-27 03:50:44 +09:00
Ishotihadus a7f748bbb5 DXTC Native Decoding Support 2018-05-27 02:45:23 +09:00
Ishotihadus 4b577b0644 Add size check in decoding image 2018-05-26 23:11:02 +09:00
Ishotihadus d6005ac502 Improve performance in decoding string and TypelessData 2018-05-26 22:36:51 +09:00
Ishotihadus e07f6ae46b RGB565 Native Decoding Support 2018-05-26 21:42:02 +09:00
Ishotihadus 363c9b01d4 Remove Assertion 2018-05-26 20:52:06 +09:00
Ishotihadus 0356d837e0 ASTC Native Decoding Support 2018-05-26 20:48:44 +09:00
Ishotihadus af71952313 Modify readme 2018-03-22 16:01:16 +09:00
Ishotihadus 789bf60fbd Add DXT5 Support 2018-03-22 16:00:13 +09:00
Ishotihadus bd8f8941bc modify readme 2018-03-03 17:45:43 +09:00
Ishotihadus eec5647b4b Add DXT1 Support 2018-03-03 16:29:10 +09:00
Ishotihadus be15092a8a Refactor ImageDecoder 2018-02-05 23:35:24 +09:00
Ishotihadus 0d49766425 fix etc2rgba8 decoding bug 2018-02-03 11:25:04 +09:00
Ishotihadus 3e0cffdfbc accelerate mikunyan-image 2018-02-03 10:51:12 +09:00
Ishotihadus c6cb66b42a 3.9.4 2018-01-31 16:35:28 +09:00
Ishotihadus 8cf35dd34d Modify readme 2018-01-31 16:33:25 +09:00
Ishotihadus 691f3ccc97 Support json outputting on mikunyan-image 2018-01-31 16:24:54 +09:00
Ishotihadus 2d660efb78 Add sprite support on mikunyan-image 2018-01-31 16:12:27 +09:00
Ishotihadus a8a5303f7b Modify readme 2018-01-16 14:16:58 +09:00
Ishotihadus 3b3db58a20 Flip decoded texture in all formats 2018-01-16 01:16:16 +09:00
Ishotihadus bcffc1b1e3 Edit readme 2018-01-16 00:47:06 +09:00
Ishotihadus 9ba90f1bd5 Add mikunyan-image 2018-01-16 00:44:26 +09:00
Ishotihadus 9686bdca95 version 3.9.3 2018-01-15 23:58:53 +09:00
Ishotihadus ad9ee60a5b Edit readme 2018-01-15 23:58:43 +09:00
Ishotihadus 68252ce102 Fix bugs on decoding ETC1 and ETC2 2018-01-15 23:56:56 +09:00
Ishotihadus afd776e836 Fix ASTC partition bug 2018-01-15 23:22:50 +09:00
Ishotihadus cb0ca77c9b version 3.9.2 2018-01-15 19:06:02 +09:00
Ishotihadus 4204f05c0b Accelerate decoding ETC1 a little 2018-01-15 19:04:45 +09:00
Ishotihadus c8c34d6c80 Edit README 2018-01-15 18:56:20 +09:00
Ishotihadus c37dd49732 Support ASTC Image 2018-01-15 18:54:48 +09:00
Ishotihadus 0e5dafa424 Fix undefined variable bug in mikunyan-json 2017-12-27 17:31:13 +09:00
Ishotihadus 7bb8f0a0a4 Fix no method errors in ImageDecoder 2017-10-08 21:04:41 +09:00
Ishotihadus 2c8f95b6fd Modify Readme 2017-09-09 00:34:36 +09:00
Ishotihadus d40f307802 Add ETC2 Support 2017-09-09 00:32:19 +09:00
71 changed files with 1751 additions and 134 deletions
+2
View File
@@ -12,4 +12,6 @@
# rspec failure tracking
.rspec_status
*.bundle
.DS_Store
+119 -38
View File
@@ -1,6 +1,10 @@
# mikunyan
Library to deserialize Unity AssetBundle files (\*.unity3d) and asset files.
A library to deserialize AssetBundle files (\*.unity3d) and asset files of Unity.
The name "Mikunyan" is derived from [Miku Maekawa](http://www.project-imas.com/wiki/Miku_Maekawa).
Ruby-Doc: http://www.rubydoc.info/gems/mikunyan/
## Installation
@@ -33,67 +37,67 @@ If you want to install development build:
```ruby
require 'mikunyan'
# load AssetBundle
# load an AssetBundle file
bundle = Mikunyan::AssetBundle.file(filename)
# you can load AssetBundle from blob
# you can also load a bundle from blob
# bundle = Mikunyan::AssetBundle.load(blob)
# select asset (normaly only one asset)
# select asset (a bundle normally contains only one asset)
asset = bundle.assets[0]
# or you can directly load asset
# or you can directly load an asset from an asset file
# asset = Mikunyan::Asset.file(filename)
# object list
# get a list of objects
list = asset.objects
# object PathIds
# get PathIds of objects
path_ids = asset.path_ids
# object container table (if available)
# get an container table of objects (if available)
containers = asset.containers
# load object (Mikunyan::ObjectValue)
# load an object (Mikunyan::ObjectValue)
obj = asset.parse_object(path_ids[0])
# simplified structure (based on Hash)
# load an object to Ruby data structures
obj_hash = asset.parse_object_simple(path_ids[0])
# hash can be easily serialized to json
# a Hash can be serialized to JSON
require 'json'
obj_hash.to_json
```
### Mikunyan::ObjectValue
`Mikunyan::ObjectValue` can be 3 types. Value, array and map.
`Mikunyan::ObjectValue` can be 3 types: value, array and key-value table.
```ruby
# You can get whether obj is value or not
# get whether obj is value or not
obj.value?
# get value
# get a value
obj.value
# same as obj.value
obj[]
# You can get whether obj is array or not
# get whether obj is array or not
obj.array?
# get array
# get an array
obj.value
# you can directly access by index
obj[0]
# If obj is map, you can get keys
# get keys (if obj is key-value table)
obj.keys
# get child object
# get child objects
obj[key]
# same as obj[key]
@@ -102,9 +106,9 @@ obj.key
### Unpack Texture2D
You can get png file directly from Texture2D asset. Output object's class is `ChunkyPNG::Image`.
You can get image data directly from Texture2D object. Output object's class is `ChunkyPNG::Image`.
Only some basic texture formats (1--5, 7, 9, 13--20, 22, 62, and 63) and ETC_RGB4 (34) are available.
Some basic texture formats (15, 7, 9, 1320, 22, 62, and 63), DXT1 (10), DXT5 (12), ETC_RGB4 (34), ETC2 (4547), and ASTC (4859) are available.
```ruby
require 'mikunyan/decoders'
@@ -112,27 +116,114 @@ require 'mikunyan/decoders'
# get some Texture2D asset
obj = asset.parse_object(path_ids[1])
# you can get Image object
# you can get image data
img = Mikunyan::ImageDecoder.decode_object(obj)
# save it!
img.save('mikunyan.png')
```
Mikunyan cannot decode ASTC files. Use `Mikunyan::ImageDecoder.create_astc_file` instead.
Mikunyan cannot decode ASTC with HDR data. Use `Mikunyan::ImageDecoder.create_astc_file` instead.
### Json / YAML Outputer
### Json / YAML Outputter
`mikunyan-json` is an executable command for converting unity3d to json.
`mikunyan-json` is an executable command for converting unity3d to JSON.
$ mikunyan-json bundle.unity3d > bundle.json
Available options:
- `--as-asset` (`-a`): interpret input file as not AssetBudnle but Asset
- `--pretty` (`-p`): prettify output json (`mikunyan-json` only)
- `--as-asset` (`-a`): interpret input file as not AssetBundle but Asset
- `--pretty` (`-p`): prettify output JSON
- `--yaml` (`-y`): YAML mode
### Image Outputter
`mikunyan-image` is an executable command for unpacking images from unity3d.
$ mikunyan-image bundle.unity3d
The console log is JSON data of output textures as follows.
```json
[
{
"name": "bg_b",
"width": 1024,
"height": 1024,
"path_id": -744818715421265689
},
{
"name": "bg_a",
"width": 1024,
"height": 1024,
"path_id": 5562124901460497987
}
]
```
If the option `--sprite` specified, `mikunyan-image` will output sprites. The logged JSON also contains sprite information.
```json
[
{
"name": "bg_a",
"width": 1024,
"height": 1024,
"path_id": 5562124901460497987,
"sprites": [
{
"name": "bg_a_0",
"x": 1.0,
"y": 303.0,
"width": 1022.0,
"height": 720.0,
"path_id": -7546240288260780845
},
{
"name": "bg_a_1",
"x": 1.0,
"y": 1.0,
"width": 720.0,
"height": 258.0,
"path_id": -5293490190204738553
}
]
},
{
"name": "bg_b",
"width": 1024,
"height": 1024,
"path_id": -744818715421265689,
"sprites": [
{
"name": "bg_b_1",
"x": 1.0,
"y": 1.0,
"width": 720.0,
"height": 258.0,
"path_id": 4884595733995530103
},
{
"name": "bg_b_0",
"x": 1.0,
"y": 303.0,
"width": 1022.0,
"height": 720.0,
"path_id": 7736251300187116441
}
]
}
]
```
Available options:
- `--as-asset` (`-a`): interpret input file as not AssetBundle but Asset
- `--outputdir` (`-o`): specify an output directory (default is a basename of input file without an extension)
- `--sprite` (`-s`): output sprites instead of textures
- `--pretty` (`-p`): prettify output JSON
## Dependencies
- [json](https://rubygems.org/gems/json)
@@ -142,19 +233,9 @@ Available options:
Mikunyan uses [oily_png](https://rubygems.org/gems/oily_png) instead of chunky_png if available.
## FAQ
## Implementation in other languages
### Sometimes unpacking fails
I'm sorry...
### Can I unpack Mesh files?
It's hard work for me...
### What mikunyan comes from?
[Miku Maekawa](http://www.project-imas.com/wiki/Miku_Maekawa).
- TypeScript: [shibunyan](https://github.com/AnemoneStar/shibunyan)
## Contributing
+9
View File
@@ -1,5 +1,14 @@
require "bundler/gem_tasks"
require "rake/extensiontask"
task :scream do
puts "みくは自分を曲げないよ!"
end
task :build => :compile
Rake::ExtensionTask.new('decoders/native') do |ext|
ext.lib_dir = 'lib/mikunyan'
end
task :default => [:clobber, :compile, :spec]
+101
View File
@@ -0,0 +1,101 @@
#!/usr/bin/env ruby
require 'mikunyan'
require 'mikunyan/decoders'
require 'fileutils'
begin
require 'usamin'
require 'usamin/overwrite'
rescue LoadError
require 'json'
end
opts = {:as_asset => false, :outputdir => nil, :sprite => false, :pretty => false}
arg = nil
i = 0
while i < ARGV.count
if ARGV[i].start_with?('-')
case ARGV[i]
when '--as-asset', '-a'
opts[:as_asset] = true
when '--outputdir', '-o'
i += 1
opts[:outputdir] = ARGV[i]
when '--sprite', '-s'
opts[:sprite] = true
when '--pretty', '-p'
opts[:pretty] = true
else
warn("Unknown option: #{ARGV[i]}")
end
else
arg = ARGV[i] unless arg
end
i += 1
end
unless arg
warn("Input file is not specified")
exit(1)
end
unless File.file?(arg)
warn("File not found: #{arg}")
exit(1)
end
assets = []
if opts[:as_asset]
assets = [Mikunyan::Asset.file(arg, File.basename(arg, '.*'))]
else
assets = Mikunyan::AssetBundle.file(arg).assets
end
outdir = opts[:outputdir] || File.basename(arg, '.*')
FileUtils.mkpath(outdir)
assets.each do |asset|
if opts[:sprite]
json = {}
textures = {}
asset.objects.select{|o| asset.object_type(o) == 'Sprite'}.each do |o|
obj = asset.parse_object(o)
next unless obj
name = obj.m_Name.value
tex_id = obj.m_RD.texture.m_PathID.value
unless textures[tex_id]
tex_obj = asset.parse_object(tex_id)
if tex_obj
textures[tex_id] = Mikunyan::ImageDecoder.decode_object(tex_obj)
json[tex_id] = {:name => tex_obj.m_Name.value, :width => textures[tex_id].width, :height => textures[tex_id].height, :path_id => tex_id, :sprites => []} if textures[tex_id]
end
end
if textures[tex_id]
x = obj.m_Rect.x.value
y = obj.m_Rect.y.value
width = obj.m_Rect.width.value
height = obj.m_Rect.height.value
json[tex_id][:sprites] << {:name => name, :x => x, :y => y, :width => width, :height => height, :path_id => o.path_id}
textures[tex_id].crop(x.round, (textures[tex_id].height - height - y).round, width.round, height.round).save("#{outdir}/#{name}.png")
end
end
puts opts[:pretty] ? JSON.pretty_generate(json.values) : JSON.generate(json.values)
else
json = []
asset.objects.select{|o| asset.object_type(o) == 'Texture2D'}.each do |o|
obj = asset.parse_object(o)
next unless obj
name = obj.m_Name.value
image = Mikunyan::ImageDecoder.decode_object(obj)
if image
json << {:name => name, :width => image.width, :height => image.height, :path_id => o.path_id}
image.save("#{outdir}/#{name}.png")
end
end
puts opts[:pretty] ? JSON.pretty_generate(json) : JSON.generate(json)
end
end
+7 -2
View File
@@ -39,7 +39,7 @@ while i < ARGV.count
i += 1
end
if option[:pretty] && option[:yaml]
if opts[:pretty] && opts[:yaml]
warn("Option --pretty is ignored if --yaml is specified.")
end
@@ -74,7 +74,12 @@ if opts[:yaml]
require 'yaml'
puts YAML.dump(assets)
else
require 'json'
begin
require 'usamin'
require 'usamin/overwrite'
rescue LoadError
require 'json'
end
assets = assets.map{|k, v| [k, obj64(v)]}.to_h
if opts[:pretty]
puts JSON.pretty_generate(assets)
+760
View File
@@ -0,0 +1,760 @@
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <ruby.h>
#include "astc.h"
static const int BitReverseTable[] = {
0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
};
static int WeightPrecTableA[] = {0, 0, 0, 3, 0, 5, 3, 0, 0, 0, 5, 3, 0, 5, 3, 0};
static int WeightPrecTableB[] = {0, 0, 1, 0, 2, 0, 1, 3, 0, 0, 1, 2, 4, 2, 3, 5};
static int CemTableA[] = {0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 5, 0, 3, 0, 0};
static int CemTableB[] = {8, 6, 5, 7, 5, 4, 6, 4, 3, 5, 3, 2, 4, 2, 1, 3, 1, 2, 1};
static inline uint_fast32_t color(uint_fast32_t r, uint_fast32_t g, uint_fast32_t b, uint_fast32_t a) {
return r | g << 8 | b << 16 | a << 24;
}
static inline uint_fast8_t bit_reverse_u8(const uint_fast8_t c, const int bits) {
return BitReverseTable[c] >> (8 - bits);
}
static inline uint_fast64_t bit_reverse_u64(const uint_fast64_t d, const int bits) {
uint_fast64_t ret =
(uint_fast64_t)BitReverseTable[d & 0xff] << 56 |
(uint_fast64_t)BitReverseTable[d >> 8 & 0xff] << 48 |
(uint_fast64_t)BitReverseTable[d >> 16 & 0xff] << 40 |
(uint_fast64_t)BitReverseTable[d >> 24 & 0xff] << 32 |
(uint_fast32_t)BitReverseTable[d >> 32 & 0xff] << 24 |
(uint_fast32_t)BitReverseTable[d >> 40 & 0xff] << 16 |
(uint_fast16_t)BitReverseTable[d >> 48 & 0xff] << 8 | BitReverseTable[d >> 56 & 0xff];
return ret >> (64 - bits);
}
static inline int getbits(const uint8_t *buf, const int bit, const int len) {
return (*(int*)(buf + bit / 8) >> (bit % 8)) & ((1 << len) - 1);
}
static inline uint_fast64_t getbits64(const uint8_t *buf, const int bit, const int len) {
uint_fast64_t mask = len == 64 ? 0xffffffffffffffff : (1ull << len) - 1;
if (len < 1)
return 0;
else if (bit >= 64)
return (*(uint_fast64_t*)(buf + 8)) >> (bit - 64) & mask;
else if (bit <= 0)
return (*(uint_fast64_t*)buf) << -bit & mask;
else if (bit + len <= 64)
return (*(uint_fast64_t*)buf) >> bit & mask;
else
return ((*(uint_fast64_t*)buf) >> bit | *(uint_fast64_t*)(buf + 8) << (64 - bit)) & mask;
}
static inline uint_fast8_t clamp(const int n) {
return n < 0 ? 0 : n > 255 ? 255 : n;
}
static inline void bit_transfer_signed(int *a, int *b) {
*b = (*b >> 1) | (*a & 0x80);
*a = (*a >> 1) & 0x3f;
if (*a & 0x20)
*a -= 0x40;
}
static inline void set_endpoint(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) {
endpoint[0] = r1;
endpoint[1] = g1;
endpoint[2] = b1;
endpoint[3] = a1;
endpoint[4] = r2;
endpoint[5] = g2;
endpoint[6] = b2;
endpoint[7] = a2;
}
static inline void set_endpoint_clamp(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) {
endpoint[0] = clamp(r1);
endpoint[1] = clamp(g1);
endpoint[2] = clamp(b1);
endpoint[3] = clamp(a1);
endpoint[4] = clamp(r2);
endpoint[5] = clamp(g2);
endpoint[6] = clamp(b2);
endpoint[7] = clamp(a2);
}
static inline void set_endpoint_blue(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) {
endpoint[0] = (r1 + b1) >> 1;
endpoint[1] = (g1 + b1) >> 1;
endpoint[2] = b1;
endpoint[3] = a1;
endpoint[4] = (r2 + b2) >> 1;
endpoint[5] = (g2 + b2) >> 1;
endpoint[6] = b2;
endpoint[7] = a2;
}
static inline void set_endpoint_blue_clamp(int endpoint[8], int r1, int g1, int b1, int a1, int r2, int g2, int b2, int a2) {
endpoint[0] = clamp((r1 + b1) >> 1);
endpoint[1] = clamp((g1 + b1) >> 1);
endpoint[2] = clamp(b1);
endpoint[3] = clamp(a1);
endpoint[4] = clamp((r2 + b2) >> 1);
endpoint[5] = clamp((g2 + b2) >> 1);
endpoint[6] = clamp(b2);
endpoint[7] = clamp(a2);
}
static inline uint_fast8_t select_color(int v0, int v1, int weight) {
return ((((v0 << 8 | v0) * (64 - weight) + (v1 << 8 | v1) * weight + 32) >> 6) * 255 + 32768) / 65536;
}
typedef struct {
int bw;
int bh;
int width;
int height;
int part_num;
int dual_plane;
int plane_selector;
int weight_range;
int weight_num; // max: 120
int cem[4];
int cem_range;
int endpoint_value_num; // max: 32
int endpoints[4][8];
int weights[144][2];
int partition[144];
} BlockData;
typedef struct {
int bits;
int nonbits;
} IntSeqData;
void decode_intseq(const uint8_t *buf, int offset, const int a, const int b, const int count, const int reverse, IntSeqData *out) {
static int mt[] = { 0, 2, 4, 5, 7 };
static int mq[] = { 0, 3, 5 };
static int TritsTable[5][256] = {
{0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 1, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2, 0, 1, 2, 0, 0, 1, 2, 1, 0, 1, 2, 2, 0, 1, 2, 2},
{0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 0, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1, 0, 0, 0, 0, 1, 1, 1, 0, 2, 2, 2, 0, 2, 2, 2, 0, 0, 0, 0, 1, 1, 1, 1, 1, 2, 2, 2, 1, 2, 2, 2, 1},
{0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 0, 0, 0, 2, 0, 0, 0, 2, 0, 0, 0, 2, 2, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 2, 2, 2, 2, 2},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2},
{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2}
};
static int QuintsTable[3][128] = {
{0, 1, 2, 3, 4, 0, 4, 4, 0, 1, 2, 3, 4, 1, 4, 4, 0, 1, 2, 3, 4, 2, 4, 4, 0, 1, 2, 3, 4, 3, 4, 4, 0, 1, 2, 3, 4, 0, 4, 0, 0, 1, 2, 3, 4, 1, 4, 1, 0, 1, 2, 3, 4, 2, 4, 2, 0, 1, 2, 3, 4, 3, 4, 3, 0, 1, 2, 3, 4, 0, 2, 3, 0, 1, 2, 3, 4, 1, 2, 3, 0, 1, 2, 3, 4, 2, 2, 3, 0, 1, 2, 3, 4, 3, 2, 3, 0, 1, 2, 3, 4, 0, 0, 1, 0, 1, 2, 3, 4, 1, 0, 1, 0, 1, 2, 3, 4, 2, 0, 1, 0, 1, 2, 3, 4, 3, 0, 1},
{0, 0, 0, 0, 0, 4, 4, 4, 1, 1, 1, 1, 1, 4, 4, 4, 2, 2, 2, 2, 2, 4, 4, 4, 3, 3, 3, 3, 3, 4, 4, 4, 0, 0, 0, 0, 0, 4, 0, 4, 1, 1, 1, 1, 1, 4, 1, 4, 2, 2, 2, 2, 2, 4, 2, 4, 3, 3, 3, 3, 3, 4, 3, 4, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3, 0, 0, 0, 0, 0, 4, 0, 0, 1, 1, 1, 1, 1, 4, 1, 1, 2, 2, 2, 2, 2, 4, 2, 2, 3, 3, 3, 3, 3, 4, 3, 3},
{0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 1, 4, 0, 0, 0, 0, 0, 0, 2, 4, 0, 0, 0, 0, 0, 0, 3, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 1, 1, 1, 1, 1, 1, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 2, 2, 2, 2, 2, 2, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4, 3, 3, 3, 3, 3, 3, 4, 4}
};
if (count <= 0)
return;
int n = 0;
if (a == 3) {
int mask = (1 << b) - 1;
int block_count = (count + 4) / 5;
int last_block_count = (count + 4) % 5 + 1;
int block_size = 8 + 5 * b;
int last_block_size = (block_size * last_block_count + 4) / 5;
if (reverse) {
for (int i = 0, p = offset; i < block_count; i++, p -= block_size) {
int now_size = (i < block_count - 1) ? block_size : last_block_size;
uint_fast64_t d = bit_reverse_u64(getbits64(buf, p - now_size, now_size), now_size);
int x = (d >> b & 3) | (d >> b * 2 & 0xc) | (d >> b * 3 & 0x10) | (d >> b * 4 & 0x60) | (d >> b * 5 & 0x80);
for (int j = 0; j < 5 && n < count; j++, n++)
out[n] = (IntSeqData){ d >> (mt[j] + b * j) & mask, TritsTable[j][x] };
}
} else {
for (int i = 0, p = offset; i < block_count; i++, p += block_size) {
uint_fast64_t d = getbits64(buf, p, (i < block_count - 1) ? block_size : last_block_size);
int x = (d >> b & 3) | (d >> b * 2 & 0xc) | (d >> b * 3 & 0x10) | (d >> b * 4 & 0x60) | (d >> b * 5 & 0x80);
for (int j = 0; j < 5 && n < count; j++, n++)
out[n] = (IntSeqData){ d >> (mt[j] + b * j) & mask, TritsTable[j][x] };
}
}
} else if (a == 5) {
int mask = (1 << b) - 1;
int block_count = (count + 2) / 3;
int last_block_count = (count + 2) % 3 + 1;
int block_size = 7 + 3 * b;
int last_block_size = (block_size * last_block_count + 2) / 3;
if (reverse) {
for (int i = 0, p = offset; i < block_count; i++, p -= block_size) {
int now_size = (i < block_count - 1) ? block_size : last_block_size;
uint_fast64_t d = bit_reverse_u64(getbits64(buf, p - now_size, now_size), now_size);
int x = (d >> b & 7) | (d >> b * 2 & 0x18) | (d >> b * 3 & 0x60);
for (int j = 0; j < 3 && n < count; j++, n++)
out[n] = (IntSeqData){ d >> (mq[j] + b * j) & mask, QuintsTable[j][x] };
}
} else {
for (int i = 0, p = offset; i < block_count; i++, p += block_size) {
uint_fast64_t d = getbits64(buf, p, (i < block_count - 1) ? block_size : last_block_size);
int x = (d >> b & 7) | (d >> b * 2 & 0x18) | (d >> b * 3 & 0x60);
for (int j = 0; j < 3 && n < count; j++, n++)
out[n] = (IntSeqData){ d >> (mq[j] + b * j) & mask, QuintsTable[j][x] };
}
}
} else {
if (reverse)
for (int p = offset - b; n < count; n++, p -= b)
out[n] = (IntSeqData){ bit_reverse_u8(getbits(buf, p, b), b), 0 };
else
for (int p = offset; n < count; n++, p += b)
out[n] = (IntSeqData){ getbits(buf, p, b), 0 };
}
}
void decode_block_params(const uint8_t *buf, BlockData *block_data) {
block_data->dual_plane = (buf[1] & 4) >> 2;
block_data->weight_range = (buf[0] >> 4 & 1) | (buf[1] << 2 & 8);
if (buf[0] & 3) {
block_data->weight_range |= buf[0] << 1 & 6;
switch (buf[0] & 0xc) {
case 0:
block_data->width = (*(int*)buf >> 7 & 3) + 4;
block_data->height = (buf[0] >> 5 & 3) + 2;
break;
case 4:
block_data->width = (*(int*)buf >> 7 & 3) + 8;
block_data->height = (buf[0] >> 5 & 3) + 2;
break;
case 8:
block_data->width = (buf[0] >> 5 & 3) + 2;
block_data->height = (*(int*)buf >> 7 & 3) + 8;
break;
case 12:
if (buf[1] & 1) {
block_data->width = (buf[0] >> 7 & 1) + 2;
block_data->height = (buf[0] >> 5 & 3) + 2;
} else {
block_data->width = (buf[0] >> 5 & 3) + 2;
block_data->height = (buf[0] >> 7 & 1) + 6;
}
break;
}
} else {
block_data->weight_range |= buf[0] >> 1 & 6;
switch ((*(int*)buf) & 0x180) {
case 0:
block_data->width = 12;
block_data->height = (buf[0] >> 5 & 3) + 2;
break;
case 0x80:
block_data->width = (buf[0] >> 5 & 3) + 2;
block_data->height = 12;
break;
case 0x100:
block_data->width = (buf[0] >> 5 & 3) + 6;
block_data->height = (buf[1] >> 1 & 3) + 6;
block_data->dual_plane = 0;
block_data->weight_range &= 7;
break;
case 0x180:
block_data->width = (buf[0] & 0x20) ? 10 : 6;
block_data->height = (buf[0] & 0x20) ? 6 : 10;
break;
}
}
block_data->part_num = (buf[1] >> 3 & 3) + 1;
block_data->weight_num = block_data->width * block_data->height;
if (block_data->dual_plane)
block_data->weight_num *= 2;
int weight_bits, config_bits, cem_base = 0;
switch (WeightPrecTableA[block_data->weight_range]) {
case 3:
weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range] + (block_data->weight_num * 8 + 4) / 5;
break;
case 5:
weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range] + (block_data->weight_num * 7 + 2) / 3;
break;
default:
weight_bits = block_data->weight_num * WeightPrecTableB[block_data->weight_range];
}
if (block_data->part_num == 1) {
block_data->cem[0] = *(int*)(buf + 1) >> 5 & 0xf;
config_bits = 17;
} else {
cem_base = *(int*)(buf + 2) >> 7 & 3;
if (cem_base == 0) {
int cem = buf[3] >> 1 & 0xf;
for (int i = 0; i < block_data->part_num; i++)
block_data->cem[i] = cem;
config_bits = 29;
} else {
for (int i = 0; i < block_data->part_num; i++)
block_data->cem[i] = ((buf[3] >> (i + 1) & 1) + cem_base - 1) << 2;
switch (block_data->part_num) {
case 2:
block_data->cem[0] |= buf[3] >> 3 & 3;
block_data->cem[1] |= getbits(buf, 126 - weight_bits, 2);
break;
case 3:
block_data->cem[0] |= buf[3] >> 4 & 1;
block_data->cem[0] |= getbits(buf, 122 - weight_bits, 2) & 2;
block_data->cem[1] |= getbits(buf, 124 - weight_bits, 2);
block_data->cem[2] |= getbits(buf, 126 - weight_bits, 2);
break;
case 4:
for (int i = 0; i < 4; i++)
block_data->cem[i] |= getbits(buf, 120 + i * 2 - weight_bits, 2);
break;
}
config_bits = 25 + block_data->part_num * 3;
}
}
if (block_data->dual_plane) {
config_bits += 2;
block_data->plane_selector = getbits(buf, cem_base ? 130 - weight_bits - block_data->part_num * 3 : 126 - weight_bits, 2);
}
int remain_bits = 128 - config_bits - weight_bits;
block_data->endpoint_value_num = 0;
for (int i = 0; i < block_data->part_num; i++)
block_data->endpoint_value_num += (block_data->cem[i] >> 1 & 6) + 2;
for (int i = 0, endpoint_bits; i < (int)(sizeof(CemTableA) / sizeof(int)); i++) {
switch (CemTableA[i]) {
case 3:
endpoint_bits = block_data->endpoint_value_num * CemTableB[i] + (block_data->endpoint_value_num * 8 + 4) / 5;
break;
case 5:
endpoint_bits = block_data->endpoint_value_num * CemTableB[i] + (block_data->endpoint_value_num * 7 + 2) / 3;
break;
default:
endpoint_bits = block_data->endpoint_value_num * CemTableB[i];
}
if (endpoint_bits <= remain_bits) {
block_data->cem_range = i;
break;
}
}
}
void decode_endpoints(const uint8_t *buf, BlockData *data) {
static int TritsTable[] = { 0, 204, 93, 44, 22, 11, 5 };
static int QuintsTable[] = { 0, 113, 54, 26, 13, 6 };
IntSeqData seq[32];
int ev[32];
decode_intseq(buf, data->part_num == 1 ? 17 : 29, CemTableA[data->cem_range], CemTableB[data->cem_range], data->endpoint_value_num, 0, seq);
switch (CemTableA[data->cem_range]) {
case 3:
for (int i = 0, b, c = TritsTable[CemTableB[data->cem_range]]; i < data->endpoint_value_num; i++) {
int a = (seq[i].bits & 1) * 0x1ff;
int x = seq[i].bits >> 1;
switch (CemTableB[data->cem_range]) {
case 1:
b = 0;
break;
case 2:
b = 0b100010110 * x;
break;
case 3:
b = x << 7 | x << 2 | x;
break;
case 4:
b = x << 6 | x;
break;
case 5:
b = x << 5 | x >> 2;
break;
case 6:
b = x << 4 | x >> 4;
break;
}
ev[i] = (a & 0x80) | ((seq[i].nonbits * c + b) ^ a) >> 2;
}
break;
case 5:
for (int i = 0, b, c = QuintsTable[CemTableB[data->cem_range]]; i < data->endpoint_value_num; i++) {
int a = (seq[i].bits & 1) * 0x1ff;
int x = seq[i].bits >> 1;
switch (CemTableB[data->cem_range]) {
case 1:
b = 0;
break;
case 2:
b = 0b100001100 * x;
break;
case 3:
b = x << 7 | x << 1 | x >> 1;
break;
case 4:
b = x << 6 | x >> 1;
break;
case 5:
b = x << 5 | x >> 3;
break;
}
ev[i] = (a & 0x80) | ((seq[i].nonbits * c + b) ^ a) >> 2;
}
break;
default:
switch (CemTableB[data->cem_range]) {
case 1:
for (int i = 0; i < data->endpoint_value_num; i++)
ev[i] = seq[i].bits * 0xff;
break;
case 2:
for (int i = 0; i < data->endpoint_value_num; i++)
ev[i] = seq[i].bits * 0x55;
break;
case 3:
for (int i = 0; i < data->endpoint_value_num; i++)
ev[i] = seq[i].bits << 5 | seq[i].bits << 2 | seq[i].bits >> 1;
break;
case 4:
for (int i = 0; i < data->endpoint_value_num; i++)
ev[i] = seq[i].bits << 4 | seq[i].bits;
break;
case 5:
for (int i = 0; i < data->endpoint_value_num; i++)
ev[i] = seq[i].bits << 3 | seq[i].bits >> 2;
break;
case 6:
for (int i = 0; i < data->endpoint_value_num; i++)
ev[i] = seq[i].bits << 2 | seq[i].bits >> 4;
break;
case 7:
for (int i = 0; i < data->endpoint_value_num; i++)
ev[i] = seq[i].bits << 1 | seq[i].bits >> 6;
break;
case 8:
for (int i = 0; i < data->endpoint_value_num; i++)
ev[i] = seq[i].bits;
break;
}
}
int *v = ev;
for (int cem = 0; cem < data->part_num; v += (data->cem[cem] / 4 + 1) * 2, cem++) {
switch (data->cem[cem]) {
case 0:
set_endpoint(data->endpoints[cem], v[0], v[0], v[0], 255, v[1], v[1], v[1], 255);
break;
case 1:
{
int l0 = (v[0] >> 2) | (v[1] & 0xc0);
int l1 = clamp(l0 + (v[1] & 0x3f));
set_endpoint(data->endpoints[cem], l0, l0, l0, 255, l1, l1, l1, 255);
}
break;
case 4:
set_endpoint(data->endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]);
break;
case 5:
bit_transfer_signed(&v[1], &v[0]);
bit_transfer_signed(&v[3], &v[2]);
v[1] += v[0];
set_endpoint_clamp(data->endpoints[cem], v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[2] + v[3]);
break;
case 6:
set_endpoint(data->endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255);
break;
case 8:
if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5])
set_endpoint(data->endpoints[cem], v[0], v[2], v[4], 255, v[1], v[3], v[5], 255);
else
set_endpoint_blue(data->endpoints[cem], v[1], v[3], v[5], 255, v[0], v[2], v[4], 255);
break;
case 9:
bit_transfer_signed(&v[1], &v[0]);
bit_transfer_signed(&v[3], &v[2]);
bit_transfer_signed(&v[5], &v[4]);
if (v[1] + v[3] + v[5] >= 0)
set_endpoint_clamp(data->endpoints[cem], v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255);
else
set_endpoint_blue_clamp(data->endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255);
break;
case 10:
set_endpoint(data->endpoints[cem], v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, v[4], v[0], v[1], v[2], v[5]);
break;
case 12:
if (v[0] + v[2] + v[4] <= v[1] + v[3] + v[5])
set_endpoint(data->endpoints[cem], v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]);
else
set_endpoint_blue(data->endpoints[cem], v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6]);
break;
case 13:
bit_transfer_signed(&v[1], &v[0]);
bit_transfer_signed(&v[3], &v[2]);
bit_transfer_signed(&v[5], &v[4]);
bit_transfer_signed(&v[7], &v[6]);
if (v[1] + v[3] + v[5] >= 0)
set_endpoint_clamp(data->endpoints[cem], v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]);
else
set_endpoint_blue_clamp(data->endpoints[cem], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]);
break;
default:
rb_raise(rb_eStandardError, "Unsupported ASTC format");
}
}
}
void decode_weights(const uint8_t *buf, BlockData *data) {
IntSeqData seq[128];
int wv[128] = {};
decode_intseq(buf, 128, WeightPrecTableA[data->weight_range], WeightPrecTableB[data->weight_range], data->weight_num, 1, seq);
if (WeightPrecTableA[data->weight_range] == 0) {
switch (WeightPrecTableB[data->weight_range]) {
case 1:
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].bits ? 63 : 0;
break;
case 2:
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].bits << 4 | seq[i].bits << 2 | seq[i].bits;
break;
case 3:
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].bits << 3 | seq[i].bits;
break;
case 4:
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].bits << 2 | seq[i].bits >> 2;
break;
case 5:
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].bits << 1 | seq[i].bits >> 4;
break;
}
for (int i = 0; i < data->weight_num; i++)
if (wv[i] > 32)
++wv[i];
} else if (WeightPrecTableB[data->weight_range] == 0) {
int s = WeightPrecTableA[data->weight_range] == 3 ? 32 : 16;
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].nonbits * s;
} else {
if (WeightPrecTableA[data->weight_range] == 3) {
switch (WeightPrecTableB[data->weight_range]) {
case 1:
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].nonbits * 50;
break;
case 2:
for (int i = 0; i < data->weight_num; i++) {
wv[i] = seq[i].nonbits * 23;
if (seq[i].bits & 2)
wv[i] += 0b1000101;
}
break;
case 3:
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].nonbits * 11 + ((seq[i].bits << 4 | seq[i].bits >> 1) & 0b1100011);
break;
}
} else if (WeightPrecTableA[data->weight_range] == 5) {
switch (WeightPrecTableB[data->weight_range]) {
case 1:
for (int i = 0; i < data->weight_num; i++)
wv[i] = seq[i].nonbits * 28;
break;
case 2:
for (int i = 0; i < data->weight_num; i++) {
wv[i] = seq[i].nonbits * 13;
if (seq[i].bits & 2)
wv[i] += 0b1000010;
}
break;
}
}
for (int i = 0; i < data->weight_num; i++) {
int a = (seq[i].bits & 1) * 0x7f;
wv[i] = (a & 0x20) | ((wv[i] ^ a) >> 2);
if (wv[i] > 32)
++wv[i];
}
}
int ds = (1024 + data->bw / 2) / (data->bw - 1);
int dt = (1024 + data->bh / 2) / (data->bh - 1);
int pn = data->dual_plane ? 2 : 1;
for (int t = 0, i = 0; t < data->bh; t++) {
for (int s = 0; s < data->bw; s++, i++) {
int gs = (ds * s * (data->width - 1) + 32) >> 6;
int gt = (dt * t * (data->height - 1) + 32) >> 6;
int fs = gs & 0xf;
int ft = gt & 0xf;
int v = (gs >> 4) + (gt >> 4) * data->width;
int w11 = (fs * ft + 8) >> 4;
int w10 = ft - w11;
int w01 = fs - w11;
int w00 = 16 - fs - ft + w11;
for (int p = 0; p < pn; p++) {
int p00 = wv[v * pn + p];
int p01 = wv[(v + 1) * pn + p];
int p10 = wv[(v + data->width) * pn + p];
int p11 = wv[(v + data->width + 1) * pn + p];
data->weights[i][p] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4;
}
}
}
}
void select_partition(const uint8_t *buf, BlockData *data) {
int small_block = data->bw * data->bh < 31;
int seed = (*(int*)buf >> 13 & 0x3ff) | (data->part_num - 1) << 10;
uint32_t rnum = seed;
rnum ^= rnum >> 15;
rnum -= rnum << 17;
rnum += rnum << 7;
rnum += rnum << 4;
rnum ^= rnum >> 5;
rnum += rnum << 16;
rnum ^= rnum >> 7;
rnum ^= rnum >> 3;
rnum ^= rnum << 6;
rnum ^= rnum >> 17;
int seeds[8];
for (int i = 0; i < 8; i++) {
seeds[i] = (rnum >> (i * 4)) & 0xF;
seeds[i] *= seeds[i];
}
int sh[2] = { seed & 2 ? 4 : 5, data->part_num == 3 ? 6 : 5 };
if (seed & 1)
for (int i = 0; i < 8; i++)
seeds[i] >>= sh[i % 2];
else
for (int i = 0; i < 8; i++)
seeds[i] >>= sh[1 - i % 2];
if (small_block) {
for (int t = 0, i = 0; t < data->bh; t++) {
for (int s = 0; s < data->bw; s++, i++) {
int x = s << 1;
int y = t << 1;
int a = (seeds[0] * x + seeds[1] * y + (rnum >> 14)) & 0x3f;
int b = (seeds[2] * x + seeds[3] * y + (rnum >> 10)) & 0x3f;
int c = data->part_num < 3 ? 0 : (seeds[4] * x + seeds[5] * y + (rnum >> 6)) & 0x3f;
int d = data->part_num < 4 ? 0 : (seeds[6] * x + seeds[7] * y + (rnum >> 2)) & 0x3f;
data->partition[i] = (a >= b && a >= c && a >= d) ? 0 : (b >= c && b >= d) ? 1 : (c >= d) ? 2 : 3;
}
}
} else {
for (int y = 0, i = 0; y < data->bh; y++) {
for (int x = 0; x < data->bw; x++, i++) {
int a = (seeds[0] * x + seeds[1] * y + (rnum >> 14)) & 0x3f;
int b = (seeds[2] * x + seeds[3] * y + (rnum >> 10)) & 0x3f;
int c = data->part_num < 3 ? 0 : (seeds[4] * x + seeds[5] * y + (rnum >> 6)) & 0x3f;
int d = data->part_num < 4 ? 0 : (seeds[6] * x + seeds[7] * y + (rnum >> 2)) & 0x3f;
data->partition[i] = (a >= b && a >= c && a >= d) ? 0 : (b >= c && b >= d) ? 1 : (c >= d) ? 2 : 3;
}
}
}
}
void applicate_color(const BlockData *data, uint32_t *outbuf) {
if (data->dual_plane) {
int ps[] = { 0, 0, 0, 0 };
ps[data->plane_selector] = 1;
if (data->part_num > 1) {
for (int i = 0; i < data->bw * data->bh; i++) {
int p = data->partition[i];
uint_fast8_t r = select_color(data->endpoints[p][0], data->endpoints[p][4], data->weights[i][ps[0]]);
uint_fast8_t g = select_color(data->endpoints[p][1], data->endpoints[p][5], data->weights[i][ps[1]]);
uint_fast8_t b = select_color(data->endpoints[p][2], data->endpoints[p][6], data->weights[i][ps[2]]);
uint_fast8_t a = select_color(data->endpoints[p][3], data->endpoints[p][7], data->weights[i][ps[3]]);
outbuf[i] = color(r, g, b, a);
}
} else {
for (int i = 0; i < data->bw * data->bh; i++) {
uint_fast8_t r = select_color(data->endpoints[0][0], data->endpoints[0][4], data->weights[i][ps[0]]);
uint_fast8_t g = select_color(data->endpoints[0][1], data->endpoints[0][5], data->weights[i][ps[1]]);
uint_fast8_t b = select_color(data->endpoints[0][2], data->endpoints[0][6], data->weights[i][ps[2]]);
uint_fast8_t a = select_color(data->endpoints[0][3], data->endpoints[0][7], data->weights[i][ps[3]]);
outbuf[i] = color(r, g, b, a);
}
}
} else if (data->part_num > 1) {
for (int i = 0; i < data->bw * data->bh; i++) {
int p = data->partition[i];
uint_fast8_t r = select_color(data->endpoints[p][0], data->endpoints[p][4], data->weights[i][0]);
uint_fast8_t g = select_color(data->endpoints[p][1], data->endpoints[p][5], data->weights[i][0]);
uint_fast8_t b = select_color(data->endpoints[p][2], data->endpoints[p][6], data->weights[i][0]);
uint_fast8_t a = select_color(data->endpoints[p][3], data->endpoints[p][7], data->weights[i][0]);
outbuf[i] = color(r, g, b, a);
}
} else {
for (int i = 0; i < data->bw * data->bh; i++) {
uint_fast8_t r = select_color(data->endpoints[0][0], data->endpoints[0][4], data->weights[i][0]);
uint_fast8_t g = select_color(data->endpoints[0][1], data->endpoints[0][5], data->weights[i][0]);
uint_fast8_t b = select_color(data->endpoints[0][2], data->endpoints[0][6], data->weights[i][0]);
uint_fast8_t a = select_color(data->endpoints[0][3], data->endpoints[0][7], data->weights[i][0]);
outbuf[i] = color(r, g, b, a);
}
}
}
void decode_block(const uint8_t *buf, const int bw, const int bh, uint32_t *outbuf) {
if (buf[0] == 0xfc && (buf[1] & 1) == 1) {
uint_fast32_t c = color(buf[9], buf[11], buf[13], buf[15]);
for (int i = 0; i < bw * bh; i++)
outbuf[i] = c;
} else {
BlockData block_data;
block_data.bw = bw;
block_data.bh = bh;
decode_block_params(buf, &block_data);
decode_endpoints(buf, &block_data);
decode_weights(buf, &block_data);
if (block_data.part_num > 1)
select_partition(buf, &block_data);
applicate_color(&block_data, outbuf);
}
}
void decode_astc(const uint8_t *data, const int w, const int h, const int bw, const int bh, uint32_t *image) {
int bcw = (w + bw - 1) / bw;
int bch = (h + bh - 1) / bh;
int clen_last = (w + bw - 1) % bw + 1;
uint32_t *buf = (uint32_t*)calloc(bw * bh, sizeof(uint32_t));
const uint8_t *ptr = data;
for (int t = 0; t < bch; t++) {
for (int s = 0; s < bcw; s++, ptr += 16) {
decode_block(ptr, bw, bh, buf);
int clen = (s < bcw - 1 ? bw : clen_last) * 4;
for (int i = 0, y = h - t * bh - 1; i < bh && y >= 0; i++, y--)
memcpy(image + y * w + s * bw, buf + i * bw, clen);
}
}
free(buf);
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef ASTC_H
#define ASTC_H
#include <stdint.h>
void decode_astc(const uint8_t*, const int, const int, const int, const int, uint32_t*);
#endif /* end of include guard: ASTC_H */
+104
View File
@@ -0,0 +1,104 @@
#include <stdint.h>
#include <string.h>
#include "dxtc.h"
static inline uint_fast32_t color(uint_fast32_t r, uint_fast32_t g, uint_fast32_t b, uint_fast32_t a) {
return r | g << 8 | b << 16 | a << 24;
}
static inline void rgb565(const uint_fast16_t c, int *r, int *g, int *b) {
*r = (c & 0xf800) >> 8;
*g = (c & 0x07e0) >> 3;
*b = (c & 0x001f) << 3;
*r |= *r >> 5;
*g |= *g >> 6;
*b |= *b >> 5;
}
static inline void decode_dxt1_block(const uint64_t *data, uint32_t *outbuf) {
int r0, g0, b0, r1, g1, b1;
int q0 = ((uint16_t*)data)[0];
int q1 = ((uint16_t*)data)[1];
rgb565(q0, &r0, &g0, &b0);
rgb565(q1, &r1, &g1, &b1);
uint_fast32_t c[4] = { color(r0, g0, b0, 255), color(r1, g1, b1, 255) };
if (q0 > q1) {
c[2] = color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 255);
c[3] = color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 255);
} else {
c[2] = color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 255);
}
uint_fast32_t d = *data >> 32;
for (int i = 0; i < 16; i++, d >>= 2)
outbuf[i] = c[d & 3];
}
void decode_dxt1(const uint64_t *data, const int w, const int h, uint32_t *image) {
int bcw = (w + 3) / 4;
int bch = (h + 3) / 4;
int clen_last = (w + 3) % 4 + 1;
uint32_t buf[16];
const uint64_t *d = data;
for (int t = 0; t < bch; t++) {
for (int s = 0; s < bcw; s++, d++) {
decode_dxt1_block(d, buf);
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
for (int i = 0, y = h - t * 4 - 1; i < 4 && y >= 0; i++, y--)
memcpy(image + y * w + s * 4, buf + i * 4, clen);
}
}
}
static inline void decode_dxt5_block(const uint64_t *data, uint32_t *outbuf) {
uint_fast32_t a[8] = { ((uint8_t*)data)[0], ((uint8_t*)data)[1] };
if (a[0] > a[1]) {
a[2] = (a[0] * 6 + a[1] ) / 7;
a[3] = (a[0] * 5 + a[1] * 2) / 7;
a[4] = (a[0] * 4 + a[1] * 3) / 7;
a[5] = (a[0] * 3 + a[1] * 4) / 7;
a[6] = (a[0] * 2 + a[1] * 5) / 7;
a[7] = (a[0] + a[1] * 6) / 7;
} else {
a[2] = (a[0] * 4 + a[1] ) / 5;
a[3] = (a[0] * 3 + a[1] * 2) / 5;
a[4] = (a[0] * 2 + a[1] * 3) / 5;
a[5] = (a[0] + a[1] * 4) / 5;
a[7] = 255;
}
for (int i = 0; i < 8; i++)
a[i] <<= 24;
int r0, g0, b0, r1, g1, b1;
int q0 = ((uint16_t*)(data + 1))[0];
int q1 = ((uint16_t*)(data + 1))[1];
rgb565(q0, &r0, &g0, &b0);
rgb565(q1, &r1, &g1, &b1);
uint_fast32_t c[4] = { color(r0, g0, b0, 0), color(r1, g1, b1, 0) };
if (q0 > q1) {
c[2] = color((r0 * 2 + r1) / 3, (g0 * 2 + g1) / 3, (b0 * 2 + b1) / 3, 0);
c[3] = color((r0 + r1 * 2) / 3, (g0 + g1 * 2) / 3, (b0 + b1 * 2) / 3, 0);
} else {
c[2] = color((r0 + r1) / 2, (g0 + g1) / 2, (b0 + b1) / 2, 0);
}
uint_fast64_t da = *data >> 16;
uint_fast32_t dc = *(data + 1) >> 32;
for (int i = 0; i < 16; i++, da >>= 3, dc >>= 2)
outbuf[i] = a[da & 7] | c[dc & 3];
}
void decode_dxt5(const uint64_t *data, const int w, const int h, uint32_t *image) {
int bcw = (w + 3) / 4;
int bch = (h + 3) / 4;
int clen_last = (w + 3) % 4 + 1;
uint32_t buf[16];
const uint64_t *d = data;
for (int t = 0; t < bch; t++) {
for (int s = 0; s < bcw; s++, d += 2) {
decode_dxt5_block(d, buf);
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
for (int i = 0, y = h - t * 4 - 1; i < 4 && y >= 0; i++, y--)
memcpy(image + y * w + s * 4, buf + i * 4, clen);
}
}
}
+9
View File
@@ -0,0 +1,9 @@
#ifndef DXTC_H
#define DXTC_H
#include <stdint.h>
void decode_dxt1(const uint64_t*, const int, const int, uint32_t*);
void decode_dxt5(const uint64_t*, const int, const int, uint32_t*);
#endif /* end of include guard: DXTC_H */
+271
View File
@@ -0,0 +1,271 @@
#include <stdint.h>
#include <string.h>
#include "etc.h"
uint_fast8_t WriteOrderTable[16] = { 0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15 };
uint_fast8_t WriteOrderTableRev[16] = { 15, 11, 7, 3, 14, 10, 6, 2, 13, 9, 5, 1, 12, 8, 4, 0 };
uint_fast8_t Etc1ModifierTable[8][2] = {{2, 8}, {5, 17}, {9, 29}, {13, 42}, {18, 60}, {24, 80}, {33, 106}, {47, 183}};
uint_fast8_t Etc1SubblockTable[2][16] = {{0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}, {0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1}};
uint_fast8_t Etc2DistanceTable[8] = {3, 6, 11, 16, 23, 32, 41, 64};
int_fast8_t Etc2AlphaModTable[16][8] = {
{-3, -6, -9, -15, 2, 5, 8, 14},
{-3, -7, -10, -13, 2, 6, 9, 12},
{-2, -5, -8, -13, 1, 4, 7, 12},
{-2, -4, -6, -13, 1, 3, 5, 12},
{-3, -6, -8, -12, 2, 5, 7, 11},
{-3, -7, -9, -11, 2, 6, 8, 10},
{-4, -7, -8, -11, 3, 6, 7, 10},
{-3, -5, -8, -11, 2, 4, 7, 10},
{-2, -6, -8, -10, 1, 5, 7, 9},
{-2, -5, -8, -10, 1, 4, 7, 9},
{-2, -4, -8, -10, 1, 3, 7, 9},
{-2, -5, -7, -10, 1, 4, 6, 9},
{-3, -4, -7, -10, 2, 3, 6, 9},
{-1, -2, -3, -10, 0, 1, 2, 9},
{-4, -6, -8, -9, 3, 5, 7, 8},
{-3, -5, -7, -9, 2, 4, 6, 8}
};
static inline uint_fast32_t color(uint_fast32_t r, uint_fast32_t g, uint_fast32_t b, uint_fast32_t a) {
return r | g << 8 | b << 16 | a << 24;
}
static inline uint_fast8_t clamp(const int n) {
return n < 0 ? 0 : n > 255 ? 255 : n;
}
static inline uint32_t applicate_color(uint_fast8_t c[3], int_fast16_t m) {
return color(clamp(c[0] + m), clamp(c[1] + m), clamp(c[2] + m), 255);
}
static inline uint32_t applicate_color_raw(uint_fast8_t c[3]) {
return color(c[0], c[1], c[2], 255);
}
static inline void decode_etc1_block(const uint8_t *data, uint32_t *outbuf) {
uint_fast8_t code[2] = { data[3] >> 5, data[3] >> 2 & 7 };
uint_fast8_t *table = Etc1SubblockTable[data[3] & 1];
uint_fast8_t c[2][3];
if (data[3] & 2) {
c[0][0] = data[0] & 0xf8;
c[0][1] = data[1] & 0xf8;
c[0][2] = data[2] & 0xf8;
c[1][0] = c[0][0] + (data[0] << 3 & 0x18) - (data[0] << 3 & 0x20);
c[1][1] = c[0][1] + (data[1] << 3 & 0x18) - (data[1] << 3 & 0x20);
c[1][2] = c[0][2] + (data[2] << 3 & 0x18) - (data[2] << 3 & 0x20);
c[0][0] |= c[0][0] >> 5;
c[0][1] |= c[0][1] >> 5;
c[0][2] |= c[0][2] >> 5;
c[1][0] |= c[1][0] >> 5;
c[1][1] |= c[1][1] >> 5;
c[1][2] |= c[1][2] >> 5;
} else {
c[0][0] = data[0] & 0xf0 | data[0] >> 4;
c[1][0] = data[0] & 0x0f | data[0] << 4;
c[0][1] = data[1] & 0xf0 | data[1] >> 4;
c[1][1] = data[1] & 0x0f | data[1] << 4;
c[0][2] = data[2] & 0xf0 | data[2] >> 4;
c[1][2] = data[2] & 0x0f | data[2] << 4;
}
uint_fast16_t j = data[6] << 8 | data[7];
uint_fast16_t k = data[4] << 8 | data[5];
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
uint_fast8_t s = table[i];
uint_fast8_t m = Etc1ModifierTable[code[s]][j & 1];
outbuf[WriteOrderTable[i]] = applicate_color(c[s], k & 1 ? -m : m);
}
}
void decode_etc1(const void *data, const int w, const int h, uint32_t *image) {
int bcw = (w + 3) / 4;
int bch = (h + 3) / 4;
int clen_last = (w + 3) % 4 + 1;
uint32_t buf[16];
const uint8_t *d = (uint8_t*)data;
for (int t = 0; t < bch; t++) {
for (int s = 0; s < bcw; s++, d += 8) {
decode_etc1_block(d, buf);
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
for (int i = 0, y = h - t * 4 - 1; i < 4 && y >= 0; i++, y--)
memcpy(image + y * w + s * 4, buf + i * 4, clen);
}
}
}
static inline void decode_etc2_block(const uint8_t *data, uint32_t *outbuf) {
uint_fast16_t j = data[6] << 8 | data[7];
uint_fast16_t k = data[4] << 8 | data[5];
uint_fast8_t c[3][3] = {};
if (data[3] & 2) {
uint_fast8_t r = data[0] & 0xf8;
int_fast16_t dr = (data[0] << 3 & 0x18) - (data[0] << 3 & 0x20);
uint_fast8_t g = data[1] & 0xf8;
int_fast16_t dg = (data[1] << 3 & 0x18) - (data[1] << 3 & 0x20);
uint_fast8_t b = data[2] & 0xf8;
int_fast16_t db = (data[2] << 3 & 0x18) - (data[2] << 3 & 0x20);
if (r + dr < 0 || r + dr > 255) {
// T
c[0][0] = data[0] << 3 & 0xc0 | data[0] << 4 & 0x30 | data[0] >> 1 & 0xc | data[0] & 3;
c[0][1] = data[1] & 0xf0 | data[1] >> 4;
c[0][2] = data[1] & 0x0f | data[1] << 4;
c[1][0] = data[2] & 0xf0 | data[2] >> 4;
c[1][1] = data[2] & 0x0f | data[2] << 4;
c[1][2] = data[3] & 0xf0 | data[3] >> 4;
uint_fast8_t d = Etc2DistanceTable[data[3] >> 1 & 6 | data[3] & 1];
uint_fast32_t color_set[4] = {
applicate_color_raw(c[0]),
applicate_color(c[1], d),
applicate_color_raw(c[1]),
applicate_color(c[1], -d)
};
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
outbuf[WriteOrderTable[i]] = color_set[k << 1 & 2 | j & 1];
} else if (g + dg < 0 || g + dg > 255) {
// H
c[0][0] = data[0] << 1 & 0xf0 | data[0] >> 3 & 0xf;
c[0][1] = data[0] << 5 & 0xe0 | data[1] & 0x10;
c[0][1] |= c[0][1] >> 4;
c[0][2] = data[1] & 8 | data[1] << 1 & 6 | data[2] >> 7;
c[0][2] |= c[0][2] << 4;
c[1][0] = data[2] << 1 & 0xf0 | data[2] >> 3 & 0xf;
c[1][1] = data[2] << 5 & 0xe0 | data[3] >> 3 & 0x10;
c[1][1] |= c[1][1] >> 4;
c[1][2] = data[3] << 1 & 0xf0 | data[3] >> 3 & 0xf;
uint_fast8_t d = data[3] & 4 | data[3] << 1 & 2;
if (c[0][0] > c[1][0] || (c[0][0] == c[1][0] && (c[0][1] > c[1][1] || (c[0][1] == c[1][1] && c[0][2] >= c[1][2]))))
++d;
d = Etc2DistanceTable[d];
uint_fast32_t color_set[4] = {
applicate_color(c[0], d),
applicate_color(c[0], -d),
applicate_color(c[1], d),
applicate_color(c[1], -d)
};
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1)
outbuf[WriteOrderTable[i]] = color_set[k << 1 & 2 | j & 1];
} else if (b + db < 0 || b + db > 255) {
// planar
c[0][0] = data[0] << 1 & 0xfc | data[0] >> 5 & 3;
c[0][1] = data[0] << 7 & 0x80 | data[1] & 0x7e | data[0] & 1;
c[0][2] = data[1] << 7 & 0x80 | data[2] << 2 & 0x60 | data[2] << 3 & 0x18 | data[3] >> 5 & 4;
c[0][2] |= c[0][2] >> 6;
c[1][0] = data[3] << 1 & 0xf8 | data[3] << 2 & 4 | data[3] >> 5 & 3;
c[1][1] = data[4] & 0xfe | data[4] >> 7;
c[1][2] = data[4] << 7 & 0x80 | data[5] >> 1 & 0x7c;
c[1][2] |= c[1][2] >> 6;
c[2][0] = data[5] << 5 & 0xe0 | data[6] >> 3 & 0x1c | data[5] >> 1 & 3;
c[2][1] = data[6] << 3 & 0xf8 | data[7] >> 5 & 0x6 | data[6] >> 4 & 1;
c[2][2] = data[7] << 2 | data[7] >> 4 & 3;
for (int y = 0, i = 0; y < 4; y++) {
for (int x = 0; x < 4; x++, i++) {
uint8_t r = clamp((x * (c[1][0] - c[0][0]) + y * (c[2][0] - c[0][0]) + 4 * c[0][0] + 2) >> 2);
uint8_t g = clamp((x * (c[1][1] - c[0][1]) + y * (c[2][1] - c[0][1]) + 4 * c[0][1] + 2) >> 2);
uint8_t b = clamp((x * (c[1][2] - c[0][2]) + y * (c[2][2] - c[0][2]) + 4 * c[0][2] + 2) >> 2);
outbuf[i] = color(r, g, b, 255);
}
}
} else {
// differential
uint_fast8_t code[2] = { data[3] >> 5, data[3] >> 2 & 7 };
uint_fast8_t *table = Etc1SubblockTable[data[3] & 1];
c[0][0] = r | r >> 5;
c[0][1] = g | g >> 5;
c[0][2] = b | b >> 5;
c[1][0] = r + dr;
c[1][1] = g + dg;
c[1][2] = b + db;
c[1][0] |= c[1][0] >> 5;
c[1][1] |= c[1][1] >> 5;
c[1][2] |= c[1][2] >> 5;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
uint_fast8_t s = table[i];
uint_fast8_t m = Etc1ModifierTable[code[s]][j & 1];
outbuf[WriteOrderTable[i]] = applicate_color(c[s], k & 1 ? -m : m);
}
}
} else {
// individual
uint_fast8_t code[2] = { data[3] >> 5, data[3] >> 2 & 7 };
uint_fast8_t *table = Etc1SubblockTable[data[3] & 1];
c[0][0] = data[0] & 0xf0 | data[0] >> 4;
c[1][0] = data[0] & 0x0f | data[0] << 4;
c[0][1] = data[1] & 0xf0 | data[1] >> 4;
c[1][1] = data[1] & 0x0f | data[1] << 4;
c[0][2] = data[2] & 0xf0 | data[2] >> 4;
c[1][2] = data[2] & 0x0f | data[2] << 4;
for (int i = 0; i < 16; i++, j >>= 1, k >>= 1) {
uint_fast8_t s = table[i];
uint_fast8_t m = Etc1ModifierTable[code[s]][j & 1];
outbuf[WriteOrderTable[i]] = applicate_color(c[s], k & 1 ? -m : m);
}
}
}
static inline void decode_etc2a8_block(const uint8_t *data, uint32_t *outbuf) {
if (data[1] & 0xf0) {
uint_fast8_t mult = data[1] >> 4;
int_fast8_t *table = Etc2AlphaModTable[data[1] & 0xf];
uint_fast64_t l =
data[7] | (uint_fast16_t)data[6] << 8 |
(uint_fast32_t)data[5] << 16 | (uint_fast32_t)data[4] << 24 |
(uint_fast64_t)data[3] << 32 | (uint_fast64_t)data[2] << 40;
for (int i = 0; i < 16; i++, l >>= 3)
((uint8_t*)(outbuf + WriteOrderTableRev[i]))[3] = clamp(data[0] + mult * table[l & 7]);
} else {
for (int i = 0; i < 16; i++)
((uint8_t*)(outbuf + i))[3] = data[0];
}
}
void decode_etc2(const void *data, const int w, const int h, uint32_t *image) {
int bcw = (w + 3) / 4;
int bch = (h + 3) / 4;
int clen_last = (w + 3) % 4 + 1;
uint32_t buf[16];
const uint8_t *d = (uint8_t*)data;
for (int t = 0; t < bch; t++) {
for (int s = 0; s < bcw; s++, d += 8) {
decode_etc2_block(d, buf);
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
for (int i = 0, y = h - t * 4 - 1; i < 4 && y >= 0; i++, y--)
memcpy(image + y * w + s * 4, buf + i * 4, clen);
}
}
}
void decode_etc2a1(const void *data, const int w, const int h, uint32_t *image) {
int bcw = (w + 3) / 4;
int bch = (h + 3) / 4;
int clen_last = (w + 3) % 4 + 1;
uint32_t buf[16];
const uint8_t *d = (uint8_t*)data;
for (int t = 0; t < bch; t++) {
for (int s = 0; s < bcw; s++, d += 9) {
decode_etc2_block(d + 1, buf);
for (int i = 0; i < 16; i++)
((uint8_t*)(buf + i))[3] = d[0];
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
for (int i = 0, y = h - t * 4 - 1; i < 4 && y >= 0; i++, y--)
memcpy(image + y * w + s * 4, buf + i * 4, clen);
}
}
}
void decode_etc2a8(const void *data, const int w, const int h, uint32_t *image) {
int bcw = (w + 3) / 4;
int bch = (h + 3) / 4;
int clen_last = (w + 3) % 4 + 1;
uint32_t buf[16];
const uint8_t *d = (uint8_t*)data;
for (int t = 0; t < bch; t++) {
for (int s = 0; s < bcw; s++, d += 16) {
decode_etc2_block(d + 8, buf);
decode_etc2a8_block(d, buf);
int clen = (s < bcw - 1 ? 4 : clen_last) * 4;
for (int i = 0, y = h - t * 4 - 1; i < 4 && y >= 0; i++, y--)
memcpy(image + y * w + s * 4, buf + i * 4, clen);
}
}
}
+11
View File
@@ -0,0 +1,11 @@
#ifndef ETC_H
#define ETC_H
#include <stdint.h>
void decode_etc1(const void*, const int, const int, uint32_t*);
void decode_etc2(const void*, const int, const int, uint32_t*);
void decode_etc2a1(const void*, const int, const int, uint32_t*);
void decode_etc2a8(const void*, const int, const int, uint32_t*);
#endif /* end of include guard: ETC_H */
+8
View File
@@ -0,0 +1,8 @@
require 'mkmf'
append_cppflags('-std=c11')
append_cppflags('-O3')
append_cppflags('-Wall')
append_cppflags('-Wextra')
append_cppflags('-Wvla')
create_makefile('mikunyan/decoders/native')
+167
View File
@@ -0,0 +1,167 @@
#include <stdlib.h>
#include <stdint.h>
#include <ruby.h>
#include "rgb.h"
#include "etc.h"
#include "astc.h"
#include "dxtc.h"
/*
* Decode image from RGB565 binary
*
* @param [String] rb_data binary to decode
* @param [Integer] size width * height
* @param [Boolean] big whether input data are big endian
* @return [String] decoded rgba binary
*/
static VALUE rb_decode_rgb565(VALUE self, VALUE rb_data, VALUE size, VALUE big) {
if (RSTRING_LEN(rb_data) < FIX2LONG(size) * 2)
rb_raise(rb_eStandardError, "Data size is not enough.");
uint8_t *image = (uint8_t*)malloc(FIX2LONG(size) * 4);
decode_rgb565((uint16_t*)RSTRING_PTR(rb_data), FIX2INT(size), RTEST(big), image);
VALUE ret = rb_str_new((char*)image, FIX2LONG(size) * 4);
free(image);
return ret;
}
/*
* Decode image from ETC1 compressed binary
*
* @param [String] rb_data binary to decode
* @param [Integer] w image width
* @param [Integer] h image height
* @return [String] decoded rgba binary
*/
static VALUE rb_decode_etc1(VALUE self, VALUE rb_data, VALUE w, VALUE h) {
if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 8)
rb_raise(rb_eStandardError, "Data size is not enough.");
uint32_t *image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t));
decode_etc1((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image);
VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t));
free(image);
return ret;
}
/*
* Decode image from ETC2 compressed binary
*
* @param [String] rb_data binary to decode
* @param [Integer] w image width
* @param [Integer] h image height
* @return [String] decoded rgba binary
*/
static VALUE rb_decode_etc2(VALUE self, VALUE rb_data, VALUE w, VALUE h) {
if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 8)
rb_raise(rb_eStandardError, "Data size is not enough.");
uint32_t *image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t));
decode_etc2((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image);
VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t));
free(image);
return ret;
}
/*
* Decode image from ETC2 Alpha1 compressed binary
*
* @param [String] rb_data binary to decode
* @param [Integer] w image width
* @param [Integer] h image height
* @return [String] decoded rgba binary
*/
static VALUE rb_decode_etc2a1(VALUE self, VALUE rb_data, VALUE w, VALUE h) {
if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 9)
rb_raise(rb_eStandardError, "Data size is not enough.");
uint32_t *image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t));
decode_etc2a8((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image);
VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t));
free(image);
return ret;
}
/*
* Decode image from ETC2 Alpha8 compressed binary
*
* @param [String] rb_data binary to decode
* @param [Integer] w image width
* @param [Integer] h image height
* @return [String] decoded rgba binary
*/
static VALUE rb_decode_etc2a8(VALUE self, VALUE rb_data, VALUE w, VALUE h) {
if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 16)
rb_raise(rb_eStandardError, "Data size is not enough.");
uint32_t *image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t));
decode_etc2a8((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image);
VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t));
free(image);
return ret;
}
/*
* Decode image from ASTC compressed binary
*
* @param [String] rb_data binary to decode
* @param [Integer] w image width
* @param [Integer] h image height
* @param [Integer] bw block width
* @param [Integer] bh block height
* @return [String] decoded rgba binary
*/
static VALUE rb_decode_astc(VALUE self, VALUE rb_data, VALUE w, VALUE h, VALUE bw, VALUE bh) {
if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + FIX2LONG(bw) - 1) / FIX2LONG(bw)) * ((FIX2LONG(h) + FIX2LONG(bh) - 1) / FIX2LONG(bh)) * 16)
rb_raise(rb_eStandardError, "Data size is not enough.");
const uint8_t *data = (uint8_t*)RSTRING_PTR(rb_data);
uint32_t *image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t));
decode_astc(data, FIX2INT(w), FIX2INT(h), FIX2INT(bw), FIX2INT(bh), image);
VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t));
free(image);
return ret;
}
/*
* Decode image from DXT1 compressed binary
*
* @param [String] rb_data binary to decode
* @param [Integer] w image width
* @param [Integer] h image height
* @return [String] decoded rgba binary
*/
static VALUE rb_decode_dxt1(VALUE self, VALUE rb_data, VALUE w, VALUE h) {
if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 8)
rb_raise(rb_eStandardError, "Data size is not enough.");
uint32_t *image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t));
decode_dxt1((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image);
VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t));
free(image);
return ret;
}
/*
* Decode image from DXT5 compressed binary
*
* @param [String] rb_data binary to decode
* @param [Integer] w image width
* @param [Integer] h image height
* @return [String] decoded rgba binary
*/
static VALUE rb_decode_dxt5(VALUE self, VALUE rb_data, VALUE w, VALUE h) {
if (RSTRING_LEN(rb_data) < ((FIX2LONG(w) + 3) / 4) * ((FIX2LONG(h) + 3) / 4) * 16)
rb_raise(rb_eStandardError, "Data size is not enough.");
uint32_t *image = (uint32_t*)calloc(FIX2LONG(w) * FIX2LONG(h), sizeof(uint32_t));
decode_dxt5((uint64_t*)RSTRING_PTR(rb_data), FIX2INT(w), FIX2INT(h), image);
VALUE ret = rb_str_new((char*)image, FIX2LONG(w) * FIX2LONG(h) * sizeof(uint32_t));
free(image);
return ret;
}
void Init_native() {
VALUE mMikunyan = rb_define_module("Mikunyan");
VALUE mDecodeHelper = rb_define_module_under(mMikunyan, "DecodeHelper");
rb_define_module_function(mDecodeHelper, "decode_rgb565", rb_decode_rgb565, 3);
rb_define_module_function(mDecodeHelper, "decode_etc1", rb_decode_etc1, 3);
rb_define_module_function(mDecodeHelper, "decode_etc2", rb_decode_etc2, 3);
rb_define_module_function(mDecodeHelper, "decode_etc2a1", rb_decode_etc2a1, 3);
rb_define_module_function(mDecodeHelper, "decode_etc2a8", rb_decode_etc2a8, 3);
rb_define_module_function(mDecodeHelper, "decode_astc", rb_decode_astc, 5);
rb_define_module_function(mDecodeHelper, "decode_dxt1", rb_decode_dxt1, 3);
rb_define_module_function(mDecodeHelper, "decode_dxt5", rb_decode_dxt5, 3);
}
+26
View File
@@ -0,0 +1,26 @@
#include <stdint.h>
static inline int is_system_little() {
int x = 1;
return *(char*)&x == 1;
}
void decode_rgb565(const uint16_t* data, const int size, const int is_big_endian, uint8_t* image) {
const uint16_t *d = data;
if (is_big_endian == is_system_little()) {
uint8_t *p = image;
for (int i = 0; i < size; i++, d++, p += 4) {
uint_fast8_t r = *d & 0x00f8;
uint_fast8_t g = (*d & 0x0007) << 5 | (*d & 0xe000) >> 11;
uint_fast8_t b = (*d & 0x1f00) >> 5;
p[0] = r | r >> 5;
p[1] = g | g >> 6;
p[2] = b | b >> 5;
p[3] = 255;
}
} else {
uint32_t *p = (uint32_t*)image;
for (int i = 0; i < size; i++, d++, p++)
*p = (*d & 0xf800) >> 8 | *d >> 13 | (*d & 0x7e0) << 5 | (*d & 0x60) << 3 | *d << 19 | (*d & 0x1c) << 14 | 0xff000000;
}
}
+8
View File
@@ -0,0 +1,8 @@
#ifndef RGB_H
#define RGB_H
#include <stdint.h>
void decode_rgb565(const uint16_t*, const int, const int, uint8_t*);
#endif /* end of include guard: RGB_H */
+22 -11
View File
@@ -10,7 +10,7 @@ module Mikunyan
# @attr_reader [Array<Integer>] add_ids ?
# @attr_reader [Array<Mikunyan::Asset::Reference>] references reference data
class Asset
attr_reader :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references
attr_reader :name, :format, :generator_version, :target_platform, :endian, :klasses, :objects, :add_ids, :references, :res_s
# Struct for representing Asset class definition
# @attr [Integer] class_id class ID
@@ -40,9 +40,10 @@ module Mikunyan
# Load Asset from binary string
# @param [String] bin binary data
# @param [String] name Asset name
# @param [String] res_s resS data
# @return [Mikunyan::Asset] deserialized Asset object
def self.load(bin, name)
r = Asset.new(name)
def self.load(bin, name, res_s = nil)
r = Asset.new(name, res_s)
r.send(:load, bin)
r
end
@@ -123,9 +124,10 @@ module Mikunyan
private
def initialize(name)
def initialize(name, res_s = nil)
@name = name
@endian = :big
@res_s = res_s
end
def load(bin)
@@ -226,19 +228,28 @@ module Mikunyan
children = type_tree[:children]
if node.array?
data = []
size = parse_object_private(br, children.find{|e| e[:name] == 'size'})
data = nil
size = parse_object_private(br, children.find{|e| e[:name] == 'size'}).value
data_type_tree = children.find{|e| e[:name] == 'data'}
size.value.times do |i|
data << parse_object_private(br, data_type_tree)
if node.type == 'TypelessData'
data = br.read(size * data_type_tree[:node].size)
else
data = size.times.map{ parse_object_private(br, data_type_tree) }
end
data = data.map{|e| e.value}.pack('C*') if node.type == 'TypelessData'
r = ObjectValue.new(node.name, node.type, br.endian, data)
elsif node.size == -1
r = ObjectValue.new(node.name, node.type, br.endian)
if children.size == 1 && children[0][:name] == 'Array' && children[0][:node].type == 'Array' && children[0][:node].array?
r.value = parse_object_private(br, children[0]).value
r.value = r.value.map{|e| e.value}.pack('C*').force_encoding("utf-8") if node.type == 'string'
if node.type == 'string'
size = parse_object_private(br, children[0][:children].find{|e| e[:name] == 'size'}).value
r.value = br.read(size * children[0][:children].find{|e| e[:name] == 'data'}[:node].size).force_encoding("utf-8")
br.align(4) if children[0][:node].flags & 0x4000 != 0
else
r.value = parse_object_private(br, children[0]).value
end
elsif node.type == 'StreamingInfo'
children.each{|child| r[child[:name]] = parse_object_private(br, child)}
r.value = @res_s.byteslice(r['offset'].value, r['size'].value) if r['path'].value == "archive:/#{name}/#{name}.resS"
else
children.each do |child|
r[child[:name]] = parse_object_private(br, child)
+4 -2
View File
@@ -84,11 +84,13 @@ module Mikunyan
asset_count = head.i32u
asset_count.times{ asset_blocks << {:offset => head.i64u, :size => head.i64u, :status => head.i32, :name => head.cstr} }
raw_data = ''
raw_data = String.new
blocks.each{|b| raw_data << uncompress(br.read(b[:c]), b[:u], b[:f])}
asset_blocks.each do |b|
asset = Asset.load(raw_data.byteslice(b[:offset], b[:size]), b[:name])
next if b[:name].end_with?('.resS')
res_s = asset_blocks.find{|e| e[:name] == "#{b[:name]}.resS"}
asset = Asset.load(raw_data.byteslice(b[:offset], b[:size]), b[:name], res_s && raw_data.byteslice(res_s[:offset], res_s[:size]))
@assets << asset
end
end
+6
View File
@@ -1 +1,7 @@
require 'mikunyan/decoders/image_decoder'
module Mikunyan
# Module for helper classes for decoding object
module DecodeHelper
end
end
+105 -79
View File
@@ -1,6 +1,6 @@
begin; require 'oily_png'; rescue LoadError; require 'chunky_png'; end
require 'bin_utils'
require 'fiddle'
require 'mikunyan/decoders/native'
module Mikunyan
# Class for image decoding tools
@@ -23,25 +23,34 @@ module Mikunyan
bin = bin.value
fmt = fmt.value
if bin.size == 0 && object['m_StreamData']
bin = object['m_StreamData'].value
return nil unless bin
end
case fmt
when 1
decode_a8(width, height, bin)
when 2
decode_argb4444(width, height, bin, endian)
when 3
decode_rgb888(width, height, bin)
decode_rgb24(width, height, bin)
when 4
decode_rgba8888(width, height, bin)
decode_rgba32(width, height, bin)
when 5
decode_argb8888(width, height, bin)
decode_argb32(width, height, bin)
when 7
decode_rgb565(width, height, bin, endian)
when 9
decode_r16(width, height, bin)
when 10
decode_dxt1(width, height, bin)
when 12
decode_dxt5(width, height, bin)
when 13
decode_rgba4444(width, height, bin, endian)
when 14
decode_bgra8888(width, height, bin)
decode_bgra32(width, height, bin)
when 15
decode_rhalf(width, height, bin, endian)
when 16
@@ -58,6 +67,24 @@ module Mikunyan
decode_rgb9e5float(width, height, bin, endian)
when 34
decode_etc1(width, height, bin)
when 45
decode_etc2rgb(width, height, bin)
when 46
decode_etc2rgba1(width, height, bin)
when 47
decode_etc2rgba8(width, height, bin)
when 48, 54
decode_astc(width, height, 4, bin)
when 49, 55
decode_astc(width, height, 5, bin)
when 50, 56
decode_astc(width, height, 6, bin)
when 51, 57
decode_astc(width, height, 8, bin)
when 52, 58
decode_astc(width, height, 10, bin)
when 53, 59
decode_astc(width, height, 12, bin)
when 62
decode_rg16(width, height, bin)
when 63
@@ -80,7 +107,7 @@ module Mikunyan
c = ((c & 0xf000) << 12) | ((c & 0x0f00) << 8) | ((c & 0x00f0) << 4) | (c & 0x000f)
BinUtils.append_int32_be!(mem, c << 4 | c)
end
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
end
# Decode image from ARGB4444 binary
@@ -96,7 +123,7 @@ module Mikunyan
c = ((c & 0x0f00) << 16) | ((c & 0x00f0) << 12) | ((c & 0x000f) << 8) | ((c & 0xf000) >> 12)
BinUtils.append_int32_be!(mem, c << 4 | c)
end
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
end
# Decode image from RGB565 binary
@@ -106,15 +133,7 @@ module Mikunyan
# @param [Symbol] endian endianness of binary
# @return [ChunkyPNG::Image] decoded image
def self.decode_rgb565(width, height, bin, endian = :big)
mem = String.new(capacity: width * height * 3)
(width * height).times do |i|
c = endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)
r = (c & 0xf800) >> 8
g = (c & 0x07e0) >> 3
b = (c & 0x001f) << 3
BinUtils.append_int8!(mem, r | r >> 5, g | g >> 6, b | b >> 5)
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_rgb565(bin, width * height, endian == :big)).flip
end
# Decode image from A8 binary
@@ -128,7 +147,7 @@ module Mikunyan
c = BinUtils.get_int8(bin, i)
BinUtils.append_int8!(mem, c, c, c)
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
end
# Decode image from R8 binary
@@ -150,7 +169,7 @@ module Mikunyan
(width * height).times do |i|
BinUtils.append_int16_int8_be!(mem, BinUtils.get_int16_be(bin, i*2), 0)
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
end
# Decode image from RGB24 binary
@@ -159,7 +178,7 @@ module Mikunyan
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_rgb24(width, height, bin)
ChunkyPNG::Image.from_rgb_stream(width, height, bin)
ChunkyPNG::Image.from_rgb_stream(width, height, bin).flip
end
# Decode image from RGBA32 binary
@@ -168,7 +187,7 @@ module Mikunyan
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_rgba32(width, height, bin)
ChunkyPNG::Image.from_rgba_stream(width, height, bin)
ChunkyPNG::Image.from_rgba_stream(width, height, bin).flip
end
# Decode image from ARGB32 binary
@@ -182,7 +201,7 @@ module Mikunyan
c = BinUtils.get_int32_be(bin, i*4)
BinUtils.append_int32_be!(mem, ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24))
end
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
end
# Decode image from BGRA32 binary
@@ -196,7 +215,7 @@ module Mikunyan
c = BinUtils.get_int32_le(bin, i*4)
BinUtils.append_int32_be!(mem, ((c & 0x00ffffff) << 8) | ((c & 0xff000000) >> 24))
end
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
end
# Decode image from R16 binary
@@ -212,7 +231,7 @@ module Mikunyan
c = f2i(r / 65535.0)
BinUtils.append_int8!(mem, c, c, c)
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
end
# Decode image from RGB9e5 binary
@@ -234,7 +253,7 @@ module Mikunyan
b = (b / 512r + 1) * (2**(e-15))
BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b))
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
end
# Decode image from R Half-float binary
@@ -249,7 +268,7 @@ module Mikunyan
c = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*2) : BinUtils.get_int16_be(bin, i*2)))
BinUtils.append_int8!(mem, c, c, c)
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
end
# Decode image from RG Half-float binary
@@ -265,7 +284,7 @@ module Mikunyan
g = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*4+2) : BinUtils.get_int16_be(bin, i*4+2)))
BinUtils.append_int8!(mem, r, g, 0)
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
end
# Decode image from RGBA Half-float binary
@@ -283,7 +302,7 @@ module Mikunyan
a = f2i(n2f(endian == :little ? BinUtils.get_int16_le(bin, i*8+6) : BinUtils.get_int16_be(bin, i*8+6)))
BinUtils.append_int8!(mem, r, g, b, a)
end
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
end
# Decode image from R float binary
@@ -299,7 +318,7 @@ module Mikunyan
c = f2i(bin.byteslice(i*4, 4).unpack(unpackstr)[0])
BinUtils.append_int8!(mem, c, c, c)
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
end
# Decode image from RG float binary
@@ -315,7 +334,7 @@ module Mikunyan
r, g = bin.byteslice(i*8, 8).unpack(unpackstr)
BinUtils.append_int8!(mem, f2i(r), f2i(g), 0)
end
ChunkyPNG::Image.from_rgb_stream(width, height, mem)
ChunkyPNG::Image.from_rgb_stream(width, height, mem).flip
end
# Decode image from RGBA float binary
@@ -331,7 +350,25 @@ module Mikunyan
r, g, b, a = bin.byteslice(i*16, 16).unpack(unpackstr)
BinUtils.append_int8!(mem, f2i(r), f2i(g), f2i(b), f2i(a))
end
ChunkyPNG::Image.from_rgba_stream(width, height, mem)
ChunkyPNG::Image.from_rgba_stream(width, height, mem).flip
end
# Decode image from DXT1 compressed binary
# @param [Integer] width image width
# @param [Integer] height image height
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_dxt1(width, height, bin)
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_dxt1(bin, width, height))
end
# Decode image from DXT5 compressed binary
# @param [Integer] width image width
# @param [Integer] height image height
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_dxt5(width, height, bin)
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_dxt5(bin, width, height))
end
# Decode image from ETC1 compressed binary
@@ -340,18 +377,44 @@ module Mikunyan
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_etc1(width, height, bin)
bw = (width + 3) / 4
bh = (height + 3) / 4
mem = Fiddle::Pointer.malloc(bw * bh * 48)
bh.times do |by|
bw.times do |bx|
block = decode_etc1_block(BinUtils.get_sint64_be(bin, (bx + by * bw) * 8))
16.times do |i|
mem[((i / 4 + bx * 4) + (i % 4 + by * 4) * bw * 4) * 3, 3] = block[i]
end
end
end
ChunkyPNG::Image.from_rgb_stream(bw * 4, bh * 4, mem.to_str).crop!(0, 0, width, height)
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc1(bin, width, height))
end
# Decode image from ETC2 compressed binary
# @param [Integer] width image width
# @param [Integer] height image height
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_etc2rgb(width, height, bin)
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2(bin, width, height))
end
# Decode image from ETC2 Alpha1 compressed binary
# @param [Integer] width image width
# @param [Integer] height image height
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_etc2rgba1(width, height, bin)
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2a1(bin, width, height))
end
# Decode image from ETC2 Alpha8 compressed binary
# @param [Integer] width image width
# @param [Integer] height image height
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_etc2rgba8(width, height, bin)
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2a8(bin, width, height))
end
# Decode image from ASTC compressed binary
# @param [Integer] width image width
# @param [Integer] height image height
# @param [Integer] blocksize block size
# @param [String] bin binary to decode
# @return [ChunkyPNG::Image] decoded image
def self.decode_astc(width, height, blocksize, bin)
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_astc(bin, width, height, blocksize, blocksize))
end
# Create ASTC file data from ObjectValue
@@ -384,43 +447,6 @@ module Mikunyan
private
Etc1ModifierTable = [[2, 8], [5, 17], [9, 29], [13, 42], [18, 60], [24, 80], [33, 106], [47, 183]]
Etc1SubblockTable = [[0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1], [0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1]]
def self.decode_etc1_block(bin)
colors = []
codes = [bin >> 37 & 7, bin >> 34 & 7]
subblocks = Etc1SubblockTable[bin[32]]
if bin[33] == 0
colors[0] = bin >> 40 & 0xf0f0f0
colors[0] = colors[0] | colors[0] >> 4
colors[1] = bin >> 36 & 0xf0f0f0
colors[1] = colors[1] | colors[1] >> 4
else
colors[0] = bin >> 40 & 0xf8f8f8
dr = (bin >> 56 & 3) - (bin >> 56 & 4)
dg = (bin >> 48 & 3) - (bin >> 48 & 4)
db = (bin >> 40 & 3) - (bin >> 40 & 4)
colors[1] = colors[0] + (dr << 19) + (dg << 11) + (db << 3)
colors[0] = colors[0] | (colors[0] >> 5 & 0x70707)
colors[1] = colors[1] | (colors[1] >> 5 & 0x70707)
end
ret = Array.new(16, 0)
16.times do |i|
modifier = Etc1ModifierTable[codes[subblocks[i]]][bin[i]]
ret[i] = etc1colormod(colors[subblocks[i]], bin[i + 16] == 0 ? modifier : -modifier)
end
ret
end
def self.etc1colormod(color, modifier)
r = (color >> 16 & 0xff) + modifier
g = (color >> 8 & 0xff) + modifier
b = (color & 0xff) + modifier
r.clamp(0, 255).chr + g.clamp(0, 255).chr + b.clamp(0, 255).chr
end
# convert 16bit float
def self.n2f(n)
case n
+2 -1
View File
@@ -22,7 +22,8 @@ module Mikunyan
node_count = br.i32u
buffer_size = br.i32u
node_count.times do
nodes << Node.new(br.i16u, br.i8u, br.i8u != 0, br.i32, br.i32, br.i32, br.i32u, br.i32u)
node = Node.new(br.i16u, br.i8u, br.i8u != 0, br.i32, br.i32, br.i32, br.i32u, br.i32u)
nodes << node
end
buffer = br.read(buffer_size)
nodes.each do |n|
+1 -1
View File
@@ -1,4 +1,4 @@
module Mikunyan
# version string
VERSION = "3.9.1"
VERSION = "3.9.6"
end
+1
View File
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]
spec.extensions = ["ext/decoders/native/extconf.rb"]
spec.add_dependency 'extlz4', '~> 0'
spec.add_dependency 'bin_utils', '~> 0'