Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 574a4f5b34 | |||
| 6313515291 | |||
| c3abc515dc | |||
| d9fb1db793 | |||
| c19aec52a3 | |||
| cc383eb5de | |||
| e9d27cb7cc | |||
| 6a0cc560f5 | |||
| ac7b0a2806 | |||
| 71dd94b76f | |||
| a7f748bbb5 | |||
| 4b577b0644 | |||
| d6005ac502 | |||
| e07f6ae46b | |||
| 363c9b01d4 | |||
| 0356d837e0 | |||
| af71952313 | |||
| 789bf60fbd | |||
| bd8f8941bc | |||
| eec5647b4b | |||
| be15092a8a | |||
| 0d49766425 | |||
| 3e0cffdfbc |
@@ -12,4 +12,6 @@
|
||||
# rspec failure tracking
|
||||
.rspec_status
|
||||
|
||||
*.bundle
|
||||
|
||||
.DS_Store
|
||||
|
||||
@@ -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`.
|
||||
|
||||
Some basic texture formats (1–5, 7, 9, 13–20, 22, 62, and 63), ETC_RGB4 (34), ETC2 (45, 47), and ASTC (48–59) are available.
|
||||
Some basic texture formats (1–5, 7, 9, 13–20, 22, 62, and 63), DXT1 (10), DXT5 (12), ETC_RGB4 (34), ETC2 (45–47), and ASTC (48–59) are available.
|
||||
|
||||
```ruby
|
||||
require 'mikunyan/decoders'
|
||||
@@ -112,7 +116,7 @@ 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!
|
||||
@@ -123,14 +127,14 @@ Mikunyan cannot decode ASTC with HDR data. Use `Mikunyan::ImageDecoder.create_as
|
||||
|
||||
### 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
|
||||
- `--as-asset` (`-a`): interpret input file as not AssetBundle but Asset
|
||||
- `--pretty` (`-p`): prettify output JSON
|
||||
- `--yaml` (`-y`): YAML mode
|
||||
|
||||
### Image Outputter
|
||||
@@ -139,7 +143,7 @@ Available options:
|
||||
|
||||
$ mikunyan-image bundle.unity3d
|
||||
|
||||
The console log is json data of output textures as below.
|
||||
The console log is JSON data of output textures as follows.
|
||||
|
||||
```json
|
||||
[
|
||||
@@ -158,7 +162,7 @@ The console log is json data of output textures as below.
|
||||
]
|
||||
```
|
||||
|
||||
If the option `--sprite` specified, `mikunyan-image` will output sprites. The log json also contains sprite information.
|
||||
If the option `--sprite` specified, `mikunyan-image` will output sprites. The logged JSON also contains sprite information.
|
||||
|
||||
```json
|
||||
[
|
||||
@@ -215,10 +219,10 @@ If the option `--sprite` specified, `mikunyan-image` will output sprites. The lo
|
||||
|
||||
Available options:
|
||||
|
||||
- `--as-asset` (`-a`): interpret input file as not AssetBudnle but Asset
|
||||
- `--outputdir` (`-o`): output directory (default is a basename of input file without an extention)
|
||||
- `--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
|
||||
- `--pretty` (`-p`): prettify output JSON
|
||||
|
||||
## Dependencies
|
||||
|
||||
@@ -229,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
|
||||
|
||||
|
||||
@@ -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]
|
||||
|
||||
+12
-8
@@ -54,15 +54,18 @@ assets.each do |asset|
|
||||
json = {}
|
||||
textures = {}
|
||||
|
||||
asset.path_ids.select{|path_id| asset.object_type(path_id) == 'Sprite'}.each do |path_id|
|
||||
obj = asset.parse_object(path_id)
|
||||
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)
|
||||
textures[tex_id] = Mikunyan::ImageDecoder.decode_object(tex_obj) if 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 tex_obj
|
||||
textures[tex_id] = Mikunyan::ImageDecoder.decode_object(tex_obj) if 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 => []}
|
||||
end
|
||||
end
|
||||
|
||||
if textures[tex_id]
|
||||
@@ -71,19 +74,20 @@ assets.each do |asset|
|
||||
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 => path_id}
|
||||
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.path_ids.select{|path_id| asset.object_type(path_id) == 'Texture2D'}.each do |path_id|
|
||||
obj = asset.parse_object(path_id)
|
||||
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 => path_id}
|
||||
json << {:name => name, :width => image.width, :height => image.height, :path_id => o.path_id}
|
||||
image.save("#{outdir}/#{name}.png")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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 x = 3, i = 0; x >= 0; x--) {
|
||||
for (int y = 3; y >= 0; y--, 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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')
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
+13
-7
@@ -226,19 +226,25 @@ 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
|
||||
else
|
||||
children.each do |child|
|
||||
r[child[:name]] = parse_object_private(br, child)
|
||||
|
||||
@@ -1,526 +0,0 @@
|
||||
require 'bin_utils'
|
||||
require 'fiddle'
|
||||
|
||||
module Mikunyan
|
||||
module DecodeHelper
|
||||
# Class for decode ASTC block
|
||||
# @attr_reader [String] data decoded data
|
||||
class AstcBlockDecoder
|
||||
attr_reader :data
|
||||
|
||||
# Decode block
|
||||
# @param [String] bin binary
|
||||
# @param [Integer] bw block width
|
||||
# @param [Integer] bh block height
|
||||
def initialize(bin, bw, bh)
|
||||
if bin[0].ord == 0xfc && bin[1].ord % 2 == 1
|
||||
@data = (bin[9] + bin[11] + bin[13] + bin[15]) * bw * bh
|
||||
else
|
||||
@d2 = BinUtils.get_int64_le(bin)
|
||||
@d1 = BinUtils.get_int64_le(bin, 8)
|
||||
@bw = bw
|
||||
@bh = bh
|
||||
|
||||
decode_block_params
|
||||
decode_endpoints
|
||||
decode_weights
|
||||
select_partition
|
||||
applicate_color
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
WeightPrecTableA = [nil, nil, 0, 3, 0, 5, 3, 0, nil, nil, 5, 3, 0, 5, 3, 0]
|
||||
WeightPrecTableB = [nil, nil, 1, 0, 2, 0, 1, 3, nil, nil, 1, 2, 4, 2, 3, 5]
|
||||
|
||||
CemTableA = [0, 0, 3, 0, 5, 3, 0, 5, 3, 0, 5, 3, 0, 5, 3, 0, 5, 3, 0]
|
||||
CemTableB = [1, 2, 1, 3, 1, 2, 4, 2, 3, 5, 3, 4, 6, 4, 5, 7, 5, 6, 8]
|
||||
|
||||
TritsTable = [
|
||||
[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]
|
||||
]
|
||||
|
||||
QuintsTable = [
|
||||
[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]
|
||||
]
|
||||
|
||||
def [](i, j = 1)
|
||||
if j < 1
|
||||
0
|
||||
elsif j == 1
|
||||
i < 64 ? @d2[i] : @d1[i - 64]
|
||||
else
|
||||
if i + j <= 64
|
||||
@d2 >> i & (1 << j) - 1
|
||||
elsif i >= 64
|
||||
@d1 >> (i - 64) & (1 << j) - 1
|
||||
else
|
||||
@d2 >> i | (@d1 & (1 << i + j - 64) - 1) << 64 - i
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def decode_block_params
|
||||
# Block Mode
|
||||
@weight_range = @d2 >> 4 & 1 | @d2 >> 6 & 8
|
||||
@dual_plane = @d2 & 0x400 == 0x400
|
||||
if @d2 & 0x3 != 0
|
||||
@weight_range |= @d2 << 1 & 6
|
||||
case @d2 & 0xc
|
||||
when 0
|
||||
@width = (@d2 >> 7 & 3) + 4
|
||||
@height = (@d2 >> 5 & 3) + 2
|
||||
when 0x4
|
||||
@width = (@d2 >> 7 & 3) + 8
|
||||
@height = (@d2 >> 5 & 3) + 2
|
||||
when 0x8
|
||||
@width = (@d2 >> 5 & 3) + 2
|
||||
@height = (@d2 >> 7 & 3) + 8
|
||||
else # 0xc
|
||||
if @d2 & 0x100 == 0
|
||||
@width = (@d2 >> 5 & 3) + 2
|
||||
@height = @d2[7] + 6
|
||||
else
|
||||
@width = @d2[7] + 2
|
||||
@height = (@d2 >> 5 & 3) + 2
|
||||
end
|
||||
end
|
||||
else
|
||||
@weight_range |= @d2 >> 1 & 6
|
||||
case @d2 & 0x180
|
||||
when 0
|
||||
@width = 12
|
||||
@height = (@d2 >> 5 & 3) + 2
|
||||
when 0x80
|
||||
@width = (@d2 >> 5 & 3) + 2
|
||||
@height = 12
|
||||
when 0x180
|
||||
@width = (@d2 & 0x20 == 0) ? 6 : 10
|
||||
@height = 16 - @width
|
||||
else # 0x100
|
||||
@width = (@d2 >> 5 & 3) + 6
|
||||
@height = (@d2 >> 9 & 3) + 6
|
||||
@dual_plane = false
|
||||
@weight_range &= 7
|
||||
end
|
||||
end
|
||||
|
||||
# Count Partitions
|
||||
@part_num = (@d2 >> 11 & 3) + 1
|
||||
|
||||
# Count Weight Bits
|
||||
@weight_num = @width * @height
|
||||
@weight_num *= 2 if @dual_plane
|
||||
case WeightPrecTableA[@weight_range]
|
||||
when 3
|
||||
@weight_bit = @weight_num * WeightPrecTableB[@weight_range] + (@weight_num * 8 + 4) / 5
|
||||
when 5
|
||||
@weight_bit = @weight_num * WeightPrecTableB[@weight_range] + (@weight_num * 7 + 2) / 3
|
||||
else # 0
|
||||
@weight_bit = @weight_num * WeightPrecTableB[@weight_range]
|
||||
end
|
||||
|
||||
# CEM
|
||||
if @part_num == 1
|
||||
@cem = [@d2 >> 13 & 0xf]
|
||||
config_bit = 17
|
||||
else
|
||||
cembase = @d2 >> 23 & 3
|
||||
if cembase == 0
|
||||
@cem = Array.new(@part_num, @d2 >> 25 & 0xf)
|
||||
config_bit = 29
|
||||
else
|
||||
@cem = (0...@part_num).map{|i| ((@d2 >> (25 + i) & 1) + cembase - 1) << 2}
|
||||
|
||||
case @part_num
|
||||
when 2
|
||||
@cem[0] |= @d2 >> 27 & 3
|
||||
@cem[1] |= self[126 - @weight_bit, 2]
|
||||
when 3
|
||||
@cem[0] |= @d2[28]
|
||||
@cem[0] |= self[123 - @weight_bit] << 1
|
||||
@cem[1] |= self[124 - @weight_bit, 2]
|
||||
@cem[2] |= self[126 - @weight_bit, 2]
|
||||
else # 4
|
||||
4.times do |i|
|
||||
@cem[i] |= self[120 + 2 * i - @weight_bit, 2]
|
||||
end
|
||||
end
|
||||
|
||||
config_bit = 25 + @part_num * 3
|
||||
end
|
||||
end
|
||||
|
||||
# Count Color Endpoint Bits
|
||||
config_bit += 2 if @dual_plane
|
||||
remain_bit = 128 - config_bit - @weight_bit
|
||||
@cem_num = @cem.map{|i| (i >> 1 & 6) + 2}.inject(:+)
|
||||
|
||||
CemTableA.count.times do |n|
|
||||
i = CemTableA.count - n - 1
|
||||
case CemTableA[i]
|
||||
when 3
|
||||
@cem_bit = @cem_num * CemTableB[i] + (@cem_num * 8 + 4) / 5
|
||||
when 5
|
||||
@cem_bit = @cem_num * CemTableB[i] + (@cem_num * 7 + 2) / 3
|
||||
else # 0
|
||||
@cem_bit = @cem_num * CemTableB[i]
|
||||
end
|
||||
|
||||
if @cem_bit <= remain_bit
|
||||
@cem_range = i
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if @dual_plane
|
||||
if @part_num == 1 || cembase == 0
|
||||
@plane_selector = self[126 - @weight_bit, 2]
|
||||
else
|
||||
@plane_selector = self[130 - @weight_bit - @part_num * 3, 2]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def decode_endpoints
|
||||
values = decode_intseq_raw(self[@part_num == 1 ? 17 : 29, @cem_bit], CemTableA[@cem_range], CemTableB[@cem_range], @cem_num).map do |e|
|
||||
unquantize_endpoint(CemTableA[@cem_range], CemTableB[@cem_range], e[0], e[1])
|
||||
end
|
||||
|
||||
@endpoint = @cem.map do |cem|
|
||||
v = values.slice!(0, (cem >> 1 & 6) + 2)
|
||||
case cem
|
||||
when 0
|
||||
[v[0], v[0], v[0], 255, v[1], v[1], v[1], 255]
|
||||
when 1
|
||||
l0 = (v[0] >> 2) | (v[1] & 0xc0)
|
||||
l1 = (l0 + (v[1] & 0x3f)).clamp(0, 255)
|
||||
[l0, l0, l0, 255, l1, l1, l1, 255]
|
||||
when 4
|
||||
[v[0], v[0], v[0], v[2], v[1], v[1], v[1], v[3]]
|
||||
when 5
|
||||
v[1], v[0] = bit_transfer_signed(v[1], v[0])
|
||||
v[3], v[2] = bit_transfer_signed(v[3], v[2])
|
||||
[v[0], v[0], v[0], v[2], v[0] + v[1], v[0] + v[1], v[0] + v[1], v[2] + v[3]].map{|i| i.clamp(0, 255)}
|
||||
when 6
|
||||
[v[0] * v[3] >> 8, v[1] * v[3] >> 8, v[2] * v[3] >> 8, 255, v[0], v[1], v[2], 255]
|
||||
when 8
|
||||
if v[0] + v[2] + v[4] <= v[1] + v[3] + v[5]
|
||||
[v[0], v[2], v[4], 255, v[1], v[3], v[5], 255]
|
||||
else
|
||||
blue_contract(v[1], v[3], v[5], 255, v[0], v[2], v[4], 255)
|
||||
end
|
||||
when 9
|
||||
v[1], v[0] = bit_transfer_signed(v[1], v[0])
|
||||
v[3], v[2] = bit_transfer_signed(v[3], v[2])
|
||||
v[5], v[4] = bit_transfer_signed(v[5], v[4])
|
||||
if v[1] + v[3] + v[5] >= 0
|
||||
[v[0], v[2], v[4], 255, v[0] + v[1], v[2] + v[3], v[4] + v[5], 255].map{|i| i.clamp(0, 255)}
|
||||
else
|
||||
blue_contract(v[0] + v[1], v[2] + v[3], v[4] + v[5], 255, v[0], v[2], v[4], 255).map{|i| i.clamp(0, 255)}
|
||||
end
|
||||
when 10
|
||||
[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]]
|
||||
when 12
|
||||
if v[0] + v[2] + v[4] <= v[1] + v[3] + v[5]
|
||||
[v[0], v[2], v[4], v[6], v[1], v[3], v[5], v[7]]
|
||||
else
|
||||
blue_contract(v[1], v[3], v[5], v[7], v[0], v[2], v[4], v[6])
|
||||
end
|
||||
when 13
|
||||
v[1], v[0] = bit_transfer_signed(v[1], v[0])
|
||||
v[3], v[2] = bit_transfer_signed(v[3], v[2])
|
||||
v[5], v[4] = bit_transfer_signed(v[5], v[4])
|
||||
v[7], v[6] = bit_transfer_signed(v[7], v[6])
|
||||
if v[1] + v[3] + v[5] >= 0
|
||||
[v[0], v[2], v[4], v[6], v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7]].map{|i| i.clamp(0, 255)}
|
||||
else
|
||||
blue_contract(v[0] + v[1], v[2] + v[3], v[4] + v[5], v[6] + v[7], v[0], v[2], v[4], v[6]).map{|i| i.clamp(0, 255)}
|
||||
end
|
||||
else
|
||||
throw NotImplementedError.new("HDR image is not supported. (CEM: #{cem})")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def bit_transfer_signed(a, b)
|
||||
b = (b >> 1) | (a & 0x80)
|
||||
a = (a >> 1) & 0x3f
|
||||
[a[5] == 1 ? a - 0x40 : a, b]
|
||||
end
|
||||
|
||||
def blue_contract(r1, g1, b1, a1, r2, g2, b2, a2)
|
||||
[(r1 + b1) >> 1, (g1 + b1) >> 1, b1, a1, (r2 + b2) >> 1, (g2 + b2) >> 1, b2, a2]
|
||||
end
|
||||
|
||||
def decode_weights
|
||||
data = (0...(@weight_bit + 7) / 8).map{|i| (((self[120 - i * 8, 8]) * 0x80200802) & 0x0884422110) * 0x0101010101 >> 32 & 0xff}
|
||||
.map.with_index{|e, i| e << i * 8}.inject(:|) & (1 << @weight_bit) - 1
|
||||
|
||||
weight_point = decode_intseq_raw(data, WeightPrecTableA[@weight_range], WeightPrecTableB[@weight_range], @weight_num).map do |e|
|
||||
unquantize_weight(WeightPrecTableA[@weight_range], WeightPrecTableB[@weight_range], e[0], e[1])
|
||||
end
|
||||
|
||||
ds = (1024 + @bw / 2) / (@bw - 1)
|
||||
dt = (1024 + @bh / 2) / (@bh - 1)
|
||||
|
||||
if @dual_plane
|
||||
@weight0 = Fiddle::Pointer.malloc(@bw * @bh)
|
||||
@weight1 = Fiddle::Pointer.malloc(@bw * @bh)
|
||||
|
||||
for t in 0...@bh
|
||||
for s in 0...@bw
|
||||
gs = (ds * s * (@width - 1) + 32) >> 6
|
||||
gt = (dt * t * (@height - 1) + 32) >> 6
|
||||
fs = gs & 0xf
|
||||
ft = gt & 0xf
|
||||
v = (gs >> 4) + (gt >> 4) * @width
|
||||
w11 = (fs * ft + 8) >> 4
|
||||
w10 = ft - w11
|
||||
w01 = fs - w11
|
||||
w00 = 16 - fs - ft + w11
|
||||
|
||||
p00 = weight_point[v * 2] || 0
|
||||
p01 = weight_point[(v + 1) * 2] || 0
|
||||
p10 = weight_point[(v + @width) * 2] || 0
|
||||
p11 = weight_point[(v + @width + 1) * 2] || 0
|
||||
@weight0[s + t * @bw] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4
|
||||
|
||||
p00 = weight_point[v * 2 + 1] || 0
|
||||
p01 = weight_point[(v + 1) * 2 + 1] || 0
|
||||
p10 = weight_point[(v + @width) * 2 + 1] || 0
|
||||
p11 = weight_point[(v + @width + 1) * 2 + 1] || 0
|
||||
@weight1[s + t * @bw] = (p00 * w00 + p01 * w01 + p10 * w10 + p11 * w11 + 8) >> 4
|
||||
end
|
||||
end
|
||||
else
|
||||
@weight = Fiddle::Pointer.malloc(@bw * @bh)
|
||||
|
||||
for t in 0...@bh
|
||||
for s in 0...@bw
|
||||
gs = (ds * s * (@width - 1) + 32) >> 6
|
||||
gt = (dt * t * (@height - 1) + 32) >> 6
|
||||
fs = gs & 0xf
|
||||
ft = gt & 0xf
|
||||
v = (gs >> 4) + (gt >> 4) * @width
|
||||
w11 = (fs * ft + 8) >> 4
|
||||
|
||||
p00 = weight_point[v] || 0
|
||||
p01 = weight_point[v + 1] || 0
|
||||
p10 = weight_point[v + @width] || 0
|
||||
p11 = weight_point[v + @width + 1] || 0
|
||||
@weight[s + t * @bw] = (p00 * (16 - fs - ft + w11) + p01 * (fs - w11) + p10 * (ft - w11) + p11 * w11 + 8) >> 4
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def select_partition
|
||||
if @part_num > 1
|
||||
small_block = @bw * @bh < 31
|
||||
|
||||
seed = (@d2 >> 13 & 0x3ff) | ((@part_num - 1) << 10)
|
||||
|
||||
rnum = seed
|
||||
rnum ^= (rnum >> 15)
|
||||
rnum = (rnum - (rnum << 17)) & 0xffffffff
|
||||
rnum = (rnum + (rnum << 7)) & 0xffffffff
|
||||
rnum = (rnum + (rnum << 4)) & 0xffffffff
|
||||
rnum ^= rnum >> 5
|
||||
rnum = (rnum + (rnum << 16)) & 0xffffffff
|
||||
rnum ^= rnum >> 7
|
||||
rnum ^= rnum >> 3
|
||||
rnum = (rnum ^ (rnum << 6)) & 0xffffffff
|
||||
rnum = rnum ^ (rnum >> 17)
|
||||
|
||||
seeds = [0, 4, 8, 12, 16, 20, 24, 28].map{|i| (rnum >> i) & 0xf}.map!{|e| e * e}
|
||||
sh = [seed & 2 == 2 ? 4 : 5, @part_num == 3 ? 6 : 5]
|
||||
sh.reverse! if seed & 1 == 0
|
||||
seeds.map!.with_index{|e, i| e >> sh[i % 2]}
|
||||
|
||||
@partition = (0...@bw * @bh).map do |i|
|
||||
x = i % @bw
|
||||
y = i / @bw
|
||||
if small_block
|
||||
x <<= 1
|
||||
y <<= 1
|
||||
end
|
||||
|
||||
a = (seeds[0] * x + seeds[1] * y + (rnum >> 14)) & 0x3f
|
||||
b = (seeds[2] * x + seeds[3] * y + (rnum >> 10)) & 0x3f
|
||||
c = @part_num < 3 ? 0 : (seeds[4] * x + seeds[5] * y + (rnum >> 6)) & 0x3f
|
||||
d = @part_num < 4 ? 0 : (seeds[6] * x + seeds[7] * y + (rnum >> 2)) & 0x3f
|
||||
|
||||
3 - [d, c, b, a].each_with_index.max[1]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def applicate_color
|
||||
mem = Fiddle::Pointer.malloc(@bw * @bh * 4)
|
||||
|
||||
if @dual_plane
|
||||
plane_arr = [0, 1, 2, 3]
|
||||
plane_arr.delete_at(@plane_selector)
|
||||
|
||||
if @partition
|
||||
(@bw * @bh).times do |i|
|
||||
part = @partition[i]
|
||||
plane_arr.each{|c| mem[i * 4 + c] = select_color(@endpoint[part][c], @endpoint[part][4 + c], @weight0[i])}
|
||||
mem[i * 4 + @plane_selector] = select_color(@endpoint[part][@plane_selector], @endpoint[part][4 + @plane_selector], @weight1[i])
|
||||
end
|
||||
else
|
||||
(@bw * @bh).times do |i|
|
||||
plane_arr.each{|c| mem[i * 4 + c] = select_color(@endpoint[0][c], @endpoint[0][4 + c], @weight0[i])}
|
||||
mem[i * 4 + @plane_selector] = select_color(@endpoint[0][@plane_selector], @endpoint[0][4 + @plane_selector], @weight1[i])
|
||||
end
|
||||
end
|
||||
elsif @partition
|
||||
(@bw * @bh).times do |i|
|
||||
part = @partition[i]
|
||||
4.times{|c| mem[i * 4 + c] = select_color(@endpoint[part][c], @endpoint[part][4 + c], @weight[i])}
|
||||
end
|
||||
else
|
||||
(@bw * @bh).times do |i|
|
||||
4.times{|c| mem[i * 4 + c] = select_color(@endpoint[0][c], @endpoint[0][4 + c], @weight[i])}
|
||||
end
|
||||
end
|
||||
|
||||
@data = mem.to_str
|
||||
end
|
||||
|
||||
def select_color(v0, v1, weight)
|
||||
v0 |= v0 << 8
|
||||
v1 |= v1 << 8
|
||||
v = (v0 * (64 - weight) + v1 * weight + 32) >> 6
|
||||
(v * 255 + 32768) / 65536
|
||||
end
|
||||
|
||||
def decode_intseq_raw(data, a, b, count)
|
||||
mask = (1 << b) - 1
|
||||
case a
|
||||
when 3
|
||||
rc = (count + 4) / 5
|
||||
ret = Array.new(rc * 5)
|
||||
m = [0, 2 + b, 4 + b * 2, 5 + b * 3, 7 + b * 4]
|
||||
rc.times do |i|
|
||||
t = (data >> b & 3) | (data >> b * 2 & 0xc) | (data >> b * 3 & 0x10) | (data >> b * 4 & 0x60) | (data >> b * 5 & 0x80)
|
||||
5.times do |j|
|
||||
ret[i * 5 + j] = [data >> m[j] & mask, TritsTable[j][t]]
|
||||
end
|
||||
data >>= b * 5 + 8
|
||||
end
|
||||
ret[0, count]
|
||||
when 5
|
||||
rc = (count + 2) / 3
|
||||
ret = Array.new(rc * 3)
|
||||
m = [0, 3 + b, 5 + b * 2]
|
||||
rc.times do |i|
|
||||
q = (data >> b & 7) | (data >> b * 2 & 0x18) | (data >> b * 3 & 0x60)
|
||||
3.times do |j|
|
||||
ret[i * 3 + j] = [data >> m[j] & mask, QuintsTable[j][q]]
|
||||
end
|
||||
data >>= b * 3 + 7
|
||||
end
|
||||
ret[0, count]
|
||||
else # 0
|
||||
(0...count).map do |i|
|
||||
[data >> b * i & mask, 0]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def unquantize_endpoint(a, b, bit, val_d)
|
||||
if a == 0
|
||||
case b
|
||||
when 1
|
||||
bit * 0xff
|
||||
when 2
|
||||
bit * 0x55
|
||||
when 3
|
||||
bit << 5 | bit << 2 | bit >> 1
|
||||
when 4
|
||||
bit << 4 | bit
|
||||
when 5
|
||||
bit << 3 | bit >> 2
|
||||
when 6
|
||||
bit << 2 | bit >> 4
|
||||
when 7
|
||||
bit << 1 | bit >> 6
|
||||
else # 8
|
||||
bit
|
||||
end
|
||||
else
|
||||
val_a = (bit & 1) * 0x1ff
|
||||
tmp_b = bit >> 1
|
||||
case b
|
||||
when 1
|
||||
val_b = 0
|
||||
val_c = a == 3 ? 204 : 113
|
||||
when 2
|
||||
val_b = a == 3 ? (0b100010110) * tmp_b : (0b100001100) * tmp_b
|
||||
val_c = a == 3 ? 93 : 54
|
||||
when 3
|
||||
val_b = a == 3 ? tmp_b << 7 | tmp_b << 2 | tmp_b : tmp_b << 7 | tmp_b << 1 | tmp_b >> 1
|
||||
val_c = a == 3 ? 44 : 26
|
||||
when 4
|
||||
val_b = tmp_b << 6 | tmp_b >> (a == 3 ? 0 : 1)
|
||||
val_c = a == 3 ? 22 : 13
|
||||
when 5
|
||||
val_b = tmp_b << 5 | tmp_b >> (a == 3 ? 2 : 3)
|
||||
val_c = a == 3 ? 11 : 6
|
||||
else # 6
|
||||
val_b = tmp_b << 4 | tmp_b >> 4
|
||||
val_c = 5
|
||||
end
|
||||
t = val_d * val_c + val_b
|
||||
t ^= val_a
|
||||
(val_a & 0x80) | (t >> 2)
|
||||
end
|
||||
end
|
||||
|
||||
def unquantize_weight(a, b, bit, val_d)
|
||||
if a == 0
|
||||
case b
|
||||
when 1
|
||||
t = bit == 1 ? 63 : 0
|
||||
when 2
|
||||
t = bit << 4 | bit << 2 | bit
|
||||
when 3
|
||||
t = bit << 3 | bit
|
||||
when 4
|
||||
t = bit << 2 | bit >> 2
|
||||
else # 5
|
||||
t = bit << 1 | bit >> 4
|
||||
end
|
||||
elsif b == 0
|
||||
t = (a == 3 ? [0, 32, 63] : [0, 16, 32, 47, 63])[val_d]
|
||||
else
|
||||
val_a = (bit & 1) * 0x7f
|
||||
case b
|
||||
when 1
|
||||
val_b = 0
|
||||
val_c = a == 3 ? 50 : 28
|
||||
when 2
|
||||
val_b = (a == 3 ? 0b1000101 : 0b1000010) * bit[1]
|
||||
val_c = a == 3 ? 23 : 13
|
||||
else # 3
|
||||
val_b = (bit << 4 | bit >> 1) & 0b1100011
|
||||
val_c = 11
|
||||
end
|
||||
t = val_d * val_c + val_b
|
||||
t ^= val_a
|
||||
t = (val_a & 0x20) | (t >> 2)
|
||||
end
|
||||
t > 32 ? t + 1 : t
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,6 @@
|
||||
begin; require 'oily_png'; rescue LoadError; require 'chunky_png'; end
|
||||
require 'bin_utils'
|
||||
require 'fiddle'
|
||||
require 'mikunyan/decoders/astc_block_decoder'
|
||||
require 'mikunyan/decoders/native'
|
||||
|
||||
module Mikunyan
|
||||
# Class for image decoding tools
|
||||
@@ -39,6 +38,10 @@ module Mikunyan
|
||||
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
|
||||
@@ -61,6 +64,8 @@ module Mikunyan
|
||||
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
|
||||
@@ -123,15 +128,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).flip
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_rgb565(bin, width * height, endian == :big)).flip
|
||||
end
|
||||
|
||||
# Decode image from A8 binary
|
||||
@@ -154,7 +151,7 @@ module Mikunyan
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_r8(width, height, bin)
|
||||
decode_a8(width, height, bin).flip
|
||||
decode_a8(width, height, bin)
|
||||
end
|
||||
|
||||
# Decode image from RG16 binary
|
||||
@@ -351,22 +348,31 @@ module Mikunyan
|
||||
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
|
||||
# @param [Integer] width image width
|
||||
# @param [Integer] height image height
|
||||
# @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
|
||||
ret = ChunkyPNG::Image.new(bh * 4, bw * 4)
|
||||
bh.times do |by|
|
||||
bw.times do |bx|
|
||||
block = decode_etc1_block(BinUtils.get_sint64_be(bin, (bx + by * bw) * 8))
|
||||
ret.replace!(ChunkyPNG::Image.from_rgb_stream(4, 4, block), by * 4, bx * 4)
|
||||
end
|
||||
end
|
||||
ret.crop(0, 0, height, width).rotate_left
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc1(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ETC2 compressed binary
|
||||
@@ -375,16 +381,16 @@ module Mikunyan
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc2rgb(width, height, bin)
|
||||
bw = (width + 3) / 4
|
||||
bh = (height + 3) / 4
|
||||
ret = ChunkyPNG::Image.new(bh * 4, bw * 4)
|
||||
bh.times do |by|
|
||||
bw.times do |bx|
|
||||
block = decode_etc2_block(BinUtils.get_sint64_be(bin, (bx + by * bw) * 8))
|
||||
ret.replace!(ChunkyPNG::Image.from_rgb_stream(4, 4, block.join), by * 4, bx * 4)
|
||||
end
|
||||
end
|
||||
ret.crop(0, 0, height, width).rotate_left
|
||||
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
|
||||
@@ -393,19 +399,7 @@ module Mikunyan
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_etc2rgba8(width, height, bin)
|
||||
bw = (width + 3) / 4
|
||||
bh = (height + 3) / 4
|
||||
ret = ChunkyPNG::Image.new(bh * 4, bw * 4)
|
||||
bh.times do |by|
|
||||
bw.times do |bx|
|
||||
alpha = decode_etc2alpha_block(BinUtils.get_int64_be(bin, (bx + by * bw) * 16))
|
||||
block = decode_etc2_block(BinUtils.get_int64_be(bin, (bx + by * bw) * 16 + 8))
|
||||
mem = String.new(capacity: 64)
|
||||
16.times{|i| mem << block[i] + alpha[i]}
|
||||
ret.replace!(ChunkyPNG::Image.from_rgba_stream(4, 4, mem), by * 4, bx * 4)
|
||||
end
|
||||
end
|
||||
ret.crop(0, 0, height, width).rotate_left
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_etc2a8(bin, width, height))
|
||||
end
|
||||
|
||||
# Decode image from ASTC compressed binary
|
||||
@@ -415,16 +409,7 @@ module Mikunyan
|
||||
# @param [String] bin binary to decode
|
||||
# @return [ChunkyPNG::Image] decoded image
|
||||
def self.decode_astc(width, height, blocksize, bin)
|
||||
bw = (width + blocksize - 1) / blocksize
|
||||
bh = (height + blocksize - 1) / blocksize
|
||||
ret = ChunkyPNG::Image.new(bw * blocksize, bh * blocksize)
|
||||
bh.times do |by|
|
||||
bw.times do |bx|
|
||||
block = DecodeHelper::AstcBlockDecoder.new(bin.byteslice((by * bw + bx) * 16, 16), blocksize, blocksize).data
|
||||
ret.replace!(ChunkyPNG::Image.from_rgba_stream(blocksize, blocksize, block), bx * blocksize, by * blocksize)
|
||||
end
|
||||
end
|
||||
ret.crop(0, 0, width, height).flip
|
||||
ChunkyPNG::Image.from_rgba_stream(width, height, DecodeHelper.decode_astc(bin, width, height, blocksize, blocksize))
|
||||
end
|
||||
|
||||
# Create ASTC file data from ObjectValue
|
||||
@@ -457,152 +442,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]]
|
||||
Etc2DistanceTable = [3, 6, 11, 16, 23, 32, 41, 64]
|
||||
Etc2AlphaModTable = [
|
||||
[-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]
|
||||
]
|
||||
|
||||
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
|
||||
|
||||
mem = Fiddle::Pointer.malloc(48)
|
||||
16.times do |i|
|
||||
modifier = Etc1ModifierTable[codes[subblocks[i]]][bin[i]]
|
||||
mem[i * 3, 3] = etc1colormod(colors[subblocks[i]], bin[i + 16] == 0 ? modifier : -modifier)
|
||||
end
|
||||
mem.to_str
|
||||
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
|
||||
|
||||
def self.decode_etc2_block(bin)
|
||||
if bin[33] == 0
|
||||
# individual
|
||||
colors = [0, 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
|
||||
codes = [bin >> 37 & 7, bin >> 34 & 7]
|
||||
subblocks = Etc1SubblockTable[bin[32]]
|
||||
(0...16).map do |i|
|
||||
modifier = Etc1ModifierTable[codes[subblocks[i]]][bin[i]]
|
||||
etc1colormod(colors[subblocks[i]], bin[i + 16] == 0 ? modifier : -modifier)
|
||||
end
|
||||
else
|
||||
r = bin >> 59
|
||||
dr = (bin >> 56 & 3) - (bin >> 56 & 4)
|
||||
g = bin >> 51 & 0x1f
|
||||
dg = (bin >> 48 & 3) - (bin >> 48 & 4)
|
||||
b = bin >> 43 & 0x1f
|
||||
db = (bin >> 40 & 3) - (bin >> 40 & 4)
|
||||
if r + dr < 0 || r + dr > 31
|
||||
# T mode
|
||||
base1 = (bin >> 49 & 0xc00) | (bin >> 48 & 0x3ff)
|
||||
base1 = (base1 & 0xf00) << 8 | (base1 & 0xf0) << 4 | (base1 & 0xf)
|
||||
base1 = (base1 << 4) | base1
|
||||
base2 = bin >> 36 & 0xfff
|
||||
base2 = (base2 & 0xf00) << 8 | (base2 & 0xf0) << 4 | (base2 & 0xf)
|
||||
base2 = (base2 << 4) | base2
|
||||
d = Etc2DistanceTable[(bin >> 33 & 6) + bin[32]]
|
||||
colors = [[base1].pack('N')[1,3], etc1colormod(base2, d), [base2].pack('N')[1,3], etc1colormod(base2, -d)]
|
||||
(0...16).map{|i| colors[bin[i] + bin[i + 16] * 2]}
|
||||
elsif g + dg < 0 || g + dg > 31
|
||||
# H mode
|
||||
base1 = (bin >> 51 & 0xfe0) | (bin >> 48 & 0x18) | (bin >> 47 & 7)
|
||||
base1 = (base1 & 0xf00) << 8 | (base1 & 0xf0) << 4 | (base1 & 0xf)
|
||||
base1 = (base1 << 4) | base1
|
||||
base2 = bin >> 35 & 0xfff
|
||||
base2 = (base2 & 0xf00) << 8 | (base2 & 0xf0) << 4 | (base2 & 0xf)
|
||||
base2 = (base2 << 4) | base2
|
||||
d = Etc2DistanceTable[bin[34] * 2 + bin[32]]
|
||||
colors = [etc1colormod(base1, d), etc1colormod(base1, -d), etc1colormod(base2, d), etc1colormod(base2, -d)]
|
||||
(0...16).map{|i| colors[bin[i] + bin[i + 16] * 2]}
|
||||
elsif b + db < 0 || b + db > 31
|
||||
# planar mode
|
||||
color_or = (bin >> 55 & 0xfc) | (bin >> 61 & 0x03)
|
||||
color_og = (bin >> 49 & 0x80) | (bin >> 48 & 0x7e) | bin[56]
|
||||
color_ob = (bin >> 41 & 0x80) | (bin >> 38 & 0x60) | (bin >> 37 & 0x1c) | (bin >> 47 & 2) | bin[44]
|
||||
color_hr = (bin >> 31 & 0xf8) | (bin >> 30 & 0x04) | (bin >> 37 & 0x03)
|
||||
color_hg = (bin >> 24 & 0xfe) | bin[31]
|
||||
color_hb = (bin >> 17 & 0xfc) | (bin >> 23 & 0x03)
|
||||
color_vr = (bin >> 11 & 0xfc) | (bin >> 17 & 0x03)
|
||||
color_vg = (bin >> 5 & 0xfe) | bin[12]
|
||||
color_vb = (bin << 2 & 0xfc) | (bin >> 4 & 0x03)
|
||||
(0...16).map do |i|
|
||||
x = i / 4
|
||||
y = i % 4
|
||||
r = (x * (color_hr - color_or) + y * (color_vr - color_or) + 4 * color_or + 2) >> 2
|
||||
g = (x * (color_hg - color_og) + y * (color_vg - color_og) + 4 * color_og + 2) >> 2
|
||||
b = (x * (color_hb - color_ob) + y * (color_vb - color_ob) + 4 * color_ob + 2) >> 2
|
||||
r.clamp(0, 255).chr + g.clamp(0, 255).chr + b.clamp(0, 255).chr
|
||||
end
|
||||
else
|
||||
# differential mode
|
||||
colors = [0, 0]
|
||||
colors[0] = bin >> 40 & 0xf8f8f8
|
||||
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)
|
||||
codes = [bin >> 37 & 7, bin >> 34 & 7]
|
||||
subblocks = Etc1SubblockTable[bin[32]]
|
||||
(0...16).map do |i|
|
||||
modifier = Etc1ModifierTable[codes[subblocks[i]]][bin[i]]
|
||||
etc1colormod(colors[subblocks[i]], bin[i + 16] == 0 ? modifier : -modifier)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def self.decode_etc2alpha_block(bin)
|
||||
if bin & 0xf0000000000000 == 0
|
||||
Array.new(16, (bin >> 56).chr)
|
||||
else
|
||||
base = bin >> 56
|
||||
mult = bin >> 52 & 0xf
|
||||
table = Etc2AlphaModTable[bin >> 48 & 0xf]
|
||||
(0...16).map{|i| (base + table[bin >> i*3 & 7] * mult).clamp(0, 255).chr}
|
||||
end
|
||||
end
|
||||
|
||||
# convert 16bit float
|
||||
def self.n2f(n)
|
||||
case n
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
module Mikunyan
|
||||
# version string
|
||||
VERSION = "3.9.4"
|
||||
VERSION = "3.9.5"
|
||||
end
|
||||
|
||||
@@ -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'
|
||||
|
||||
Reference in New Issue
Block a user