CCF-画字符-详细的注释

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;

public class Main3 {
    static int R, G, B;

    static String reset = "\\x1B\\x5B\\x30\\x6D";

    public static void main(String[] args) {
        //test();
        run();
    }

    public static void test() {
        //测试代码
    }

    public static void run() {
        FastIO io = new FastIO();
        io.init(System.in);
        int width = io.nextInt(), height = io.nextInt();
        int pw = io.nextInt(), qh = io.nextInt();
        int[][][] image = new int[height][width][3];
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                hexToRGB(io.next());
                image[i][j][0] = R;
                image[i][j][1] = G;
                image[i][j][2] = B;
            }
        }
        int count = pw * qh;//一个小块中像素点的数量
        int rAvg = 0, gAvg = 0, bAvg = 0;
        int preR = 0, preG = 0, preB = 0;
        StringBuilder result = new StringBuilder();
        boolean first = true;
        for (int i = 0; i < height; i += qh) {
            for (int j = 0; j < width; j += pw) {
                int rSum = 0, gSum = 0, bSum = 0;
                for (int y = i; y < i + qh; y++) {
                    for (int x = j; x < j + pw; x++) {
                        rSum += image[y][x][0];
                        gSum += image[y][x][1];
                        bSum += image[y][x][2];
                    }
                }
                rAvg = rSum / count;
                gAvg = gSum / count;
                bAvg = bSum / count;
                if (first) {
                    //一行的开始,此时背景色已经是默认色,所以如果该块等于默认色,则什么都不做。
                    // 只有该块的颜色不等于默认色的时候才输出默认色
                    if (!(rAvg == 0 && gAvg == 0 && bAvg == 0)) {
                        changeBackground(rAvg, gAvg, bAvg, result);
                    }
                    first = false;
                } else if (!(rAvg == preR && gAvg == preG && bAvg == preB)) {
                    //如果该块的颜色不等于上一块的颜色,且等于默认色输出重置序列
                    if (rAvg == 0 && gAvg == 0 && bAvg == 0) {
                        result.append(reset);
                    } else {
                        //如果该块的颜色不等于上一块的颜色,且不等于默认色,那么改变输出背景色序列
                        changeBackground(rAvg, gAvg, bAvg, result);
                    }
                }
                result.append("\\x20");
                preR = rAvg;
                preG = gAvg;
                preB = bAvg;
            }
            //如果该行结束后时的背景色不等于默认色,则输出重置序列
            if (!(rAvg == 0 && gAvg == 0 && bAvg == 0)) {
                result.append(reset);
            }
            first = true;
            //添加一个换行符
            result.append("\\x0A");
        }
        //只在最后输出结果,而不是每一行就输出一次,这样可以减少IO次数,节约用时
        System.out.print(result.toString());
    }

    /**
     * 将颜色的16进制转换成(R,G,B)的形式
     * @param hex 颜色的16进制值
     */
    public static void hexToRGB(String hex) {
        if (hex.length() == 4) {
            //#abc->#aabbcc的情况
            R = Integer.parseInt(hex.substring(1, 2) + hex.substring(1, 2), 16);
            G = Integer.parseInt(hex.substring(2, 3) + hex.substring(2, 3), 16);
            B = Integer.parseInt(hex.substring(3, 4) + hex.substring(3, 4), 16);
        } else if (hex.length() == 2) {
            //#a->#aaaaaa的情况
            R = Integer.parseInt(hex.substring(1,2)+hex.substring(1,2), 16);
            G = R;
            B = R;
        } else {
            //#aabbcc的情况
            R = Integer.parseInt(hex.substring(1, 3), 16);
            G = Integer.parseInt(hex.substring(3, 5), 16);
            B = Integer.parseInt(hex.substring(5, 7), 16);
        }
    }

    /**
     * 改变背景色
     */
    public static void changeBackground(int R, int G, int B, StringBuilder hexOut) {
        // ESC[48;2
        hexOut.append("\\x1B\\x5B\\x34\\x38\\x3B\\x32\\x3B");
        getHexOrder(R,hexOut);
        // 3B对应字符 ;
        hexOut.append("\\x3B");
        getHexOrder(G, hexOut);
        hexOut.append("\\x3B");
        getHexOrder(B, hexOut);
        // 6D 对应字符m
        hexOut.append("\\x6D");
    }

    /**
     * 获取一个分量每位对应的16进制,并且按照输出的顺序进行拼接
     * @param a 颜色int值
     */
    public static void getHexOrder(int a, StringBuilder hexOut) {
        if (a >= 100) {
            //该颜色分量有3位
            hexOut.append("\\x");
            // 48对应ACSII字符表中的'0' 字符,因为我们最终的输出时一个字符对应的转义序列,而不是10进制的int值的16进制,所以需要加上48
            hexOut.append(Integer.toHexString(a / 100+48));
            hexOut.append("\\x");
            hexOut.append(Integer.toHexString(a / 10 % 10+48));
            hexOut.append("\\x");
            hexOut.append(Integer.toHexString(a % 10+48));
        } else if (a >= 10) {
            //颜色分量有2位
            hexOut.append("\\x");
            hexOut.append(Integer.toHexString(a / 10+48));
            hexOut.append("\\x");
            hexOut.append(Integer.toHexString(a % 10+48));
        }else {
            //颜色分量只有一位
            hexOut.append("\\x");
            hexOut.append(Integer.toHexString(48 + a));
        }
    }


    static class FastIO {
        BufferedReader reader;
        StringTokenizer tokenizer;

        void init(InputStream inputStream) {
            reader = new BufferedReader(new InputStreamReader(inputStream));
            tokenizer = new StringTokenizer("");
        }

        //读入一个字符串
        String next() {
            while (!tokenizer.hasMoreTokens()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return tokenizer.nextToken();
        }

        //读入一整行
        String nextLine() {
            while (!tokenizer.hasMoreElements()) {
                try {
                    tokenizer = new StringTokenizer(reader.readLine());
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            return tokenizer.nextToken("\n");
        }

        //读入int类型数据
        int nextInt() {
            return Integer.parseInt(next());
        }

        //读入double类型数据
        double nextDouble() {
            return Double.parseDouble(next());
        }
    }
}

posted @ 2019-11-23 18:00  Mario_Xue  阅读(171)  评论(0编辑  收藏  举报