File: astc_rgbm_blockify.cpp

package info (click to toggle)
astc-encoder 2.1%2Bds-3
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 36,948 kB
  • sloc: cpp: 20,204; python: 2,598; makefile: 156; sh: 15
file content (157 lines) | stat: -rw-r--r-- 5,260 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// SPDX-License-Identifier: Apache-2.0
// ----------------------------------------------------------------------------
// Copyright 2019-2020 Arm Limited
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.
// ----------------------------------------------------------------------------

// This is a utility tool to blockify the M channel of an RGBM image, writing
// the result back to a file on disk.
//
// This tool requires stb_image.h and stb_image_write.h single header libraries
// from: https://github.com/nothings/stb.

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>

#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

#define STB_IMAGE_WRITE_IMPLEMENTATION
#include "stb_image_write.h"

// Force val[N+1] to be contrained to not be less than val[N]*(R/256)
void impose_ratio(uint8_t *data, int length, int stride, uint32_t ratio) {
    uint8_t val = *data;
    for (int i = 1; i < length; i++) {
        data += stride;

        int min_val = (val * ratio) >> 8;
        uint8_t vl2 = *data;
        if (vl2 < min_val) {
            *data = vl2 = min_val;
        }
        val = vl2;
    }
}

int main( int argc, char **argv ) {
    // Parse command line
    if (argc < 5) {
        printf("Usage:\n"
            "   %s <source> <dest> <blocksize> <ratio>\n"
            "where the arguments are:\n"
            "\n"
            "<source>    : Source image in RGBM representation.\n"
            "<dest>      : Destination PNG image file to write.\n"
            "<blocksize> : ASTC block size we are blockifying for; e.g. 6x6.\n"
            "<ratio>     : The maximum permitted ratio of M between two\n"
            "              adjacent blocks. Can be set to any value between\n"
            "              0.01 and 0.99. Recommend 0.7 to start with.\n"
            , argv[0]);
        exit(1);
    }

    int x_blockdim, y_blockdim;
    if (sscanf(argv[3], "%dx%d", &x_blockdim, &y_blockdim) < 2) {
        printf("Blockdim must be of form WxH; e.g. 8x4\n");
        exit(1);
    }

    double ratio_f = atof(argv[4]);
    if (ratio_f < 0.01 || ratio_f > 0.99) {
        printf("Ratio parameter must be in range 0.01 to 0.99\n");
    }

    int ratio_i = (int)floor(ratio_f * 256.0 + 0.5);

    // Load the image
    int xdim, ydim, ncomp;
    uint8_t *data = (uint8_t *)stbi_load(argv[1], &xdim, &ydim, &ncomp, 4);
    if (!data) {
        printf("Failed to load image \"%s\"\n", data );
        exit(1);
    }

    int x_blockcount = (xdim + x_blockdim - 1) / x_blockdim;
    int y_blockcount = (ydim + y_blockdim - 1) / y_blockdim;
    int y_leftover = y_blockdim * y_blockcount - ydim;

    uint8_t *mbuf = (uint8_t *)calloc(x_blockcount * y_blockcount, 1);

    // For each block, obtain the maximum M-value
    for (int y = 0; y < ydim; y++) {
        int ctr = 0;
        uint8_t *s = data + 4 * y * xdim + 3;
        uint8_t *d = mbuf + ((y + y_leftover) / y_blockdim) * x_blockcount;

        int accum = *d;
        for (int x = 0; x < xdim; x++) {
            uint8_t p = s[4*x];
            if (p > accum) {
                accum = p;
            }

            if(++ctr == x_blockdim) {
                *d++ = accum;
                accum = *d;
                ctr = 0;
            }
        }

        if (ctr != 0) {
            *d = accum;
        }
    }

    // Impose ratio restriction on M-values in adjacent blocks.
    for (int y = 0;  y < y_blockcount; y++) {
        impose_ratio(mbuf + y * x_blockcount, x_blockcount, 1, ratio_i);
        impose_ratio(mbuf + ((y + 1) * x_blockcount) - 1, x_blockcount, - 1, ratio_i);
    }

    for (int x = 0; x < x_blockcount; x++) {
        impose_ratio(mbuf + x, y_blockcount, x_blockcount, ratio_i);
        impose_ratio(mbuf + x + (x_blockcount * (y_blockcount - 1)), y_blockcount, -x_blockcount, ratio_i);
    }

    // For each pixel, scale the pixel RGB values based on chosen M
    for (int y = 0; y < ydim; y++) {
        int ctr = 0;
        uint8_t *s = data + 4 * y * xdim;
        uint8_t *d = mbuf + ((y + y_leftover) / y_blockdim) * x_blockcount;
        int mm = *d++;
        for (int x = 0; x < xdim; x++) {
            uint8_t m = s[4 * x + 3];
            if(m != mm) {
                uint8_t r = s[4 * x];
                uint8_t g = s[4 * x + 1];
                uint8_t b = s[4 * x + 2];
                s[4 * x]     = (r * m + (mm >> 1)) / mm;
                s[4 * x + 1] = (g * m + (mm >> 1)) / mm;
                s[4 * x + 2] = (b * m + (mm >> 1)) / mm;
                s[4 * x + 3] = mm;
            }

            if (++ctr == x_blockdim) {
                ctr = 0;
                mm = *d++;
            }
        }
    }

    // Write out the result
    stbi_write_png(argv[2], xdim, ydim, 4, data, 4*xdim);
    return 0;
}