/
Dixix404
/
Image_Compression
Обзор
Документация
Войти
/
Dixix404
/
Image_Compression
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
master
Library/src/main/java/org/example/RLECompressor.java
226 строк
8 KB
Dixix404
Algorithm RLE
15 дек 2025, 23:40
15 дек 2025, 23:40
4cb9d66
Код
Авторство
О чём код?
package org.example; import java.io.*; public class RLECompressor implements Compressor { private static final byte[] SIGNATURE = {'R', 'L', 'E', 0}; private static final int MAX_RUN_LENGTH = 255; private static final byte RUN_MARKER = (byte) 255; // Маркер повторяющейся серии private static final byte LITERAL_MARKER = (byte) 0; // Маркер неповторяющейся серии @Override public String getName() { return "rle"; } /** * @param inputBmpPath путь к входному BMP файлу * @param outputPath путь к выходному сжатому файлу * @throws IOException при ошибках чтения/записи */ @Override public void compress(String inputBmpPath, String outputPath) throws IOException { // Читаем BMP BMPImage image = new BMPImage(); image.read(inputBmpPath); // Получаем пиксельные данные byte[][] pixels = image.getPixels(); int height = image.getHeight(); int width = image.getWidth(); // Преобразуем 2D массив в 1D для RLE кодирования byte[] flatPixels = flattenPixels(pixels); // Применяем RLE кодирование ByteArrayOutputStream compressedData = new ByteArrayOutputStream(); applyRLE(flatPixels, compressedData); // Записываем сжатый файл try (DataOutputStream dos = new DataOutputStream( new BufferedOutputStream(new FileOutputStream(outputPath)))) { // Записываем сигнатуру dos.write(SIGNATURE); // Записываем BMP заголовки dos.write(image.getFileHeader()); dos.write(image.getInfoHeader()); // Записываем размеры dos.writeInt(width); dos.writeInt(height); // Записываем сжатые данные dos.write(compressedData.toByteArray()); } } /** * @param inputPath путь к сжатому файлу * @param outputBmpPath путь к выходному BMP файлу * @throws IOException при ошибках чтения/записи */ @Override public void decompress(String inputPath, String outputBmpPath) throws IOException { try (DataInputStream dis = new DataInputStream( new BufferedInputStream(new FileInputStream(inputPath)))) { // Читаем и проверяем сигнатуру byte[] signature = new byte[4]; dis.readFully(signature); if (!java.util.Arrays.equals(signature, SIGNATURE)) { throw new IOException("Неверная сигнатура файла (не RLE формат)"); } // Читаем BMP заголовки byte[] fileHeader = new byte[14]; byte[] infoHeader = new byte[40]; dis.readFully(fileHeader); dis.readFully(infoHeader); // Читаем размеры int width = dis.readInt(); int height = dis.readInt(); // Декодируем RLE данные byte[] decodedPixels = decodeRLE(dis, height * width * 3); // Преобразуем обратно в 2D массив byte[][] pixels = unflattenPixels(decodedPixels, height, width); // Создаём и записываем BMP BMPImage image = new BMPImage(); image.setHeaders(fileHeader, infoHeader, width, height); image.setPixels(pixels); image.write(outputBmpPath); } } /** * @param data исходные данные * @param out выходной поток для закодированных данных * @throws IOException при ошибках записи */ private void applyRLE(byte[] data, ByteArrayOutputStream out) throws IOException { int i = 0; while (i < data.length) { // Проверяем, есть ли серия повторяющихся байтов int runLength = 1; while (i + runLength < data.length && data[i] == data[i + runLength] && runLength < MAX_RUN_LENGTH) { runLength++; } if (runLength >= 3) { // Записываем повторяющуюся серию out.write(RUN_MARKER); out.write(runLength); out.write(data[i]); i += runLength; } else { // Ищем неповторяющуюся последовательность int literalStart = i; int literalLength = 0; while (i < data.length && literalLength < MAX_RUN_LENGTH) { // Проверяем, не начинается ли серия повторений int lookahead = 1; while (i + lookahead < data.length && data[i] == data[i + lookahead] && lookahead < 3) { lookahead++; } if (lookahead >= 3) { // Найдена серия - останавливаемся break; } literalLength++; i++; } // Записываем неповторяющуюся последовательность out.write(LITERAL_MARKER); out.write(literalLength); out.write(data, literalStart, literalLength); } } } /** * @param dis входной поток * @param expectedSize ожидаемый размер декодированных данных * @return декодированные данные * @throws IOException при ошибках чтения или неверном формате */ private byte[] decodeRLE(DataInputStream dis, int expectedSize) throws IOException { ByteArrayOutputStream decoded = new ByteArrayOutputStream(expectedSize); while (decoded.size() < expectedSize) { int marker = dis.readUnsignedByte(); int length = dis.readUnsignedByte(); if (marker == (RUN_MARKER & 0xFF)) { // Повторяющаяся серия byte value = dis.readByte(); for (int i = 0; i < length; i++) { decoded.write(value); } } else if (marker == (LITERAL_MARKER & 0xFF)) { // Неповторяющаяся последовательность for (int i = 0; i < length; i++) { decoded.write(dis.readByte()); } } else { throw new IOException("Неверный RLE маркер: " + marker); } } return decoded.toByteArray(); } /** * @param pixels массив [height][width * 3] * @return плоский массив байтов */ private byte[] flattenPixels(byte[][] pixels) { int totalBytes = 0; for (byte[] row : pixels) { totalBytes += row.length; } byte[] flat = new byte[totalBytes]; int offset = 0; for (byte[] row : pixels) { System.arraycopy(row, 0, flat, offset, row.length); offset += row.length; } return flat; } /** * @param flat плоский массив байтов * @param height высота изображения * @param width ширина изображения * @return массив [height][width * 3] */ private byte[][] unflattenPixels(byte[] flat, int height, int width) { byte[][] pixels = new byte[height][width * 3]; int offset = 0; for (int y = 0; y < height; y++) { System.arraycopy(flat, offset, pixels[y], 0, width * 3); offset += width * 3; } return pixels; } }