Какой растровый формат Notepad++ использует для своих иконок (чтобы я мог их изменить)?


Я использую Notepad++, чтобы сделать большую часть моего кода в настоящее время. Тем не менее, я хотел бы изменить его иконки, просто для настройки на самом деле.

Итак, что я сделал, так это открыл .exe с Resource Hacker , и посмотреть, где значки были. Они, кажется, в формате bitmap, что странно, потому что у них есть прозрачность, и замена их любым "Bitmap" (BM заголовок, "Windows Bitmap") формат, который я знаю, не работает (значок не отображается).

Поэтому я хотел бы спросить, который формат этих файлов, и как я могу их создать?

Образец растровых изображений можно найти в репозитории Notepad++ :
newFile http://sourceforge.net/p/notepad-plus/code/735/tree/trunk/PowerEditor/src/icons/newFile.bmp?format=raw openFile http://sourceforge.net/p/notepad-plus/code/735/tree/trunk/PowerEditor/src/icons/openFile.bmp?format=raw saveFile http://sourceforge.net/p/notepad-plus/code/735/tree/trunk/PowerEditor/src/icons/saveFile.bmp?format=raw saveAll http://sourceforge.net/p/notepad-plus/code/735/tree/trunk/PowerEditor/src/icons/saveAll.bmp?format=raw закрыть файл http://sourceforge.net/p/notepad-plus/code/735/tree/trunk/PowerEditor/src/icons/closeFile.bmp?format=raw closeAll http://sourceforge.net/p/notepad-plus/code/735/tree/trunk/PowerEditor/src/icons/closeAll.bmp?format=raw

Как вы видите, они имеют серый матовый цвет, что заставляет меня думать, что альфа-канал хранится в формате каким-то нестандартным способом. (или, он имеет двоичную прозрачность, и #C0C0C0 означает прозрачный).

Он также довольно большой, имея более 5 байт на каждый представленный пиксель (!).

Вот как выглядит заголовок первого растрового изображения выше:

Шестигранная Мастерская

Есть идеи? Просто знать, что это за формат, было бы достаточно.

2 3

2 ответа:

Это 8bpp (8 бит на пиксель) индексированное растровое изображение. После "BM" идет 52-байтовый заголовок, палитра из 256 цветов и индексированные пиксельные данные. Каждый цвет - 4 байта, в формате BGR_ (четвертый байт не используется; если бы это был альфа-канал, вы бы увидели FF вместо 00).

Индексированные пиксельные данные начинаются с 0x436. Все просто: один байт представляет один индекс в палитре. Например, первые несколько байтов-07 07 18 18. 0x07 заставит этот пиксель использовать 8-й цвет в палитра, которая является #C0C0C0; и 0x18 заставит этот пиксель использовать 25-й цвет в палитре, которая является #CECECE.

Я предполагаю, что прозрачность обрабатывается так, как вы догадались. Я вижу 07, где бы ни был прозрачный пиксель, и соответствующий цвет в палитре - #C0C0C0.

Обратите внимание, что пиксельные данные хранятся в перевернутом виде. То есть первые 16 байт при смещении 0x436 представляют собой нижний ряд пикселей .

Это объясняется более подробно здесь: http://en.wikipedia.org/wiki/BMP_file_format#File_structure

EDIT: что касается способа их получения, просто попросите ваше любимое программное обеспечение для редактирования изображений экспортировать изображение в виде 256-цветного растрового изображения (или растрового изображения 8BPP, в зависимости от того, что доступно). Обратите внимание, что Microsoft Paint будет искажать цвета, Если вы сохраните его после рисования, поэтому сохраните его до того, как вы на самом деле нарисуете что-либо.

Все, что указал Джефф, верно и будет оставаться принятым ответом. Тем не менее, на случай, если кто-то еще захочет настроить NP++, я сделал небольшой конвертер, который вы можете использовать для преобразования любого 8-битного индексированного растрового изображения в тот, который вы можете использовать с NP++ (через resource hacker или аналогичный).

Он закодирован на Javascript, тестируется только с Chrome, и код ужасно неаккуратный: JsFiddle

Чтобы использовать его, создайте 8-битное 16x16 индексированное растровое изображение в вашей любимой программе, и перетащите его в эту штуку. Он выведет некоторые детали отладки и даст вам ссылку, чтобы вы могли "скачать" совместимое растровое изображение (но все это происходит на стороне клиента с HTML5).

Edit: так жалуется, что я поставил ссылку на JsFiddle, не разместив здесь соответствующий код, так что я его опубликую. Вот он, полностью автономный HTML:

<!DOCTYPE html>
<html>
    <head>
        <title>Endoblast</title>
        <style>
            html { background: #F8F8F8; }
            #drop-zone {
                position: fixed;
                left: 0;
                right: 0;
                bottom: 0;
                top: 0;
                border: 4px dashed #DDD;
                z-index: -1;
            }
            #result { font: 12px Consolas; padding-bottom: 240px; }
            .color { margin-right: -7px; position: relative; cursor: default; }
            .color > .top, .color > .bottom { position: absolute; left: 0; right: 0; height: 2px; }
            .color > .top    { position: absolute; top:    0; background: rgba(255, 255, 255, 0.2); }
            .color > .bottom { position: absolute; bottom: 0; background: rgba(  0,   0,   0, 0.05); }
        </style>
    </head>
    <body>
        <div id="drop-zone"></div>
        <p id="result"></p>
        <script>

            if (window.File && window.FileReader && window.FileList && window.Blob) {

                function Readr(bytes) {

                    var data = bytes;

                    var offset = 0;

                    this.nextStr = function(amount) {

                        var result = []

                        while (amount--) result.push(data[offset++]);

                        return toStr(result);

                    }

                    this.nextInt = function(amount) {

                        if (amount == 1 || !amount) return data[offset++];

                        if (amount == 2) return toInt16(data[offset++], data[offset++]);

                        if (amount == 4) return toInt32(data[offset++], data[offset++], data[offset++], data[offset++]);

                        return next(amount);

                    }

                    var next = this.next = function(amount) {

                        var result = []

                        while (amount--) result.push(data[offset++]);

                        return result;

                    }

                }

                function Color(r, g, b) {

                    this.r = r;
                    this.g = g;
                    this.b = b;

                }

                function toStr(bytes) {
                    var result = '';
                    for (var i = 0; i < bytes.length; i++) result += String.fromCharCode(bytes[i]);
                    return result;
                }

                function toInt16(b1, b2) { return (b1 << 0) + (b2 << 8); }

                function toInt32(b1, b2, b3, b4) { return (b1 << 0) + (b2 << 8) + (b3 << 16) + (b4 << 24); }

                function fromInt16(int16) {

                    return [
                         int16 & parseInt('00FF', 16),
                        (int16 & parseInt('FF00', 16)) >> 8
                    ];

                }

                function fromInt32(int32) {

                    return [
                         int32 & parseInt('000000FF', 16),
                        (int32 & parseInt('0000FF00', 16)) >> 8,
                        (int32 & parseInt('00FF0000', 16)) >> 16,
                        (int32 & parseInt('FF000000', 16)) >> 24
                    ];

                }

                function log(text) { document.getElementById('result').innerHTML += text + '\n<br/>'; }

                function logHtml(html) { document.getElementById('result').innerHTML += html + '<br/>'; }

                var bitCounts = {
                    '1': '1-bit',
                    '2': '2-bit',
                    '4': '4-bit',
                    '8': '8-bit',
                    '16': '16-bit',
                    '24': '24-bit',
                    '32': '32-bit'
                };

                var compressions = {
                    '0': 'RGB (uncompressed)'
                };

                function handleFileSelect(evt) {

                    evt.stopPropagation();
                    evt.preventDefault();

                    var files = evt.dataTransfer.files; // FileList object.

                    var reader = new FileReader();

                    reader.onload = function (event) {

                        var b = [].slice.call(new Uint8Array(event.target.result));

                        var r = new Readr(b);

                        if (r.nextStr(2) == 'BM')
                            log('BM header found.');
                        else
                            return log('File is not a BMP.');

                        var fileSize = r.nextInt(4);

                        log('File size: ' + fileSize + ' Bytes.');

                        var reserved = r.next(4)

                        var offset = r.nextInt(4);

                        log('Image data offset: ' + offset + ' Bytes.');
                        log('Image data size: ' + (fileSize - offset) + ' Bytes.');

                        var headerSize = r.nextInt(4);

                        log('DIB header size: ' + headerSize + ' Bytes.');

                        var header = r.next(headerSize - 4);

                        var hr = new Readr(header);

                        var width = hr.nextInt(4);
                        var height = hr.nextInt(4);

                        log('Image width: ' + width + 'px.');
                        log('Image height: ' + height + 'px.');

                        var planes = hr.nextInt(2);

                        log('Color planes: ' + planes + '.');

                        var bitCount = bitCounts[hr.nextInt(2)] || 'unknown';
                        var compression = compressions[hr.nextInt(4)] || 'unknown';

                        log('Bit depth: ' + bitCount + '.');
                        log('Compression: ' + compression + '.');

                        var pixelCount = hr.nextInt(4);

                        log('Pixel count: ' + pixelCount + '.');

                        var yPPM = hr.nextInt(4);
                        var xPPM = hr.nextInt(4);

                        var colorTableSize = hr.nextInt(4);

                        log('Color table size: ' + colorTableSize + '.');

                        var importantColors = hr.nextInt(4);

                        log('Important colors in color table: ' + importantColors + '.');

                        var colorTable = r.next(colorTableSize * 4);

                        var cr = new Readr(colorTable);

                        var colors = [];

                        for (var i = 0; i < colorTableSize; i++) {

                            var B = cr.nextInt(),
                                G = cr.nextInt(),
                                R = cr.nextInt(),
                                _ = cr.nextInt();

                            colors.push(new Color(R, G, B));

                        }

                        function createColorCell(r, g, b, desc) {

                            var title = 'title="' + desc + ': rgb(' + r + ', ' + g + ', '  + b + ')"'

                            return '<span class="color" ' + title + ' style="background:rgb(' + r + ',' + g + ',' + b + ')">'
                                 + '<span class="top"></span><span class="bottom"></span>&nbsp;&nbsp;</span> ';

                        }

                        var cells = '';

                        for (var i = 0; i < colors.length; i++) {

                            var c = colors[i];

                            cells += createColorCell(c.r, c.g, c.b, 'Color at index ' + i + ' in the color table');

                        }

                        if (colorTableSize) logHtml('Colors in the color table: ' + cells); else return log('No color table.');

                        log('Bitmap data, mapped to the color table is shown below.');

                        var bitmapCells = '';

                        var bitmapRows = [];

                        var indexes = [];

                        for (var y = height; y > 0; y--) {

                            for (var x = 0; x < width; x++) {

                                var index = r.nextInt();

                                indexes.push(index);

                                var color = colors[index];

                                var desc = 'Pixel x' + x + ', y' + y + ', mapped to index ' + index + ' in the color table, '
                                         + 'which has colors rgb(' + color.r + ', ' + color.g + ', ' + color.g + ')';

                                bitmapCells += createColorCell(color.r, color.g, color.b, desc);

                            }

                            bitmapRows.push(bitmapCells);
                            bitmapCells = '';

                        }

                        for (var row = height - 1; row > -1; row--) {

                            logHtml(bitmapRows[row]);

                        }

                        var fixedData = new ArrayBuffer(1334);

                        var fixed = new Uint8Array(fixedData);

                        var fixedOffset = 0;

                        function s8(b) { fixed[fixedOffset++] = b; }

                        function s16(i16) {
                            var bytes = fromInt16(i16);
                            s8(bytes[0]);
                            s8(bytes[1]);
                        }

                        function s32(i32) {
                            var bytes = fromInt32(i32);
                            s8(bytes[0]);
                            s8(bytes[1]);
                            s8(bytes[2]);
                            s8(bytes[3]);
                        }

                        // Now we build a fixed up bitmap.

                        s16(19778); // header.
                        s32(1334); // file size.
                        s32(0); // reserved.
                        s32(1078); // DIB data offset.
                        s32(40); // DIB header size.
                        s32(16); // width.
                        s32(16); // height.
                        s16(1); // planes.
                        s16(8); // bit depth.
                        s32(0); // compression.
                        s32(256); // image size.
                        s32(0); // Xpels.
                        s32(0); // Ypels.
                        s32(256); // Colors used.
                        s32(256); // Important colors.

                        for (var i = 0; i < 1024; i++) {

                            var c = colorTable[i];

                            if (typeof c !== 'undefined')
                                s8(colorTable[i])
                            else
                                s8(((i % 4) == 3) ? 0 : 255);

                        }

                        for (var i = 0; i < 256; i++) s8(indexes[i]);

                        window.URL = window.webkitURL || window.URL;
                        window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder;
                        var file = new window.BlobBuilder();

                        file.append(fixed);

                        var a = document.createElement('a');
                        a.href = window.URL.createObjectURL(file.getBlob('image/bmp'));
                        a.download = 'fixed.bmp';
                        a.textContent = 'Download Notepad++ compatible bitmap';
                        document.getElementById('result').appendChild(a);

                    };

                    reader.readAsArrayBuffer(files[0]);

                }

                function handleDragOver(evt) {
                    evt.stopPropagation();
                    evt.preventDefault();
                    evt.dataTransfer.dropEffect = 'copy';
                }

                var dropZone = document.getElementById('drop-zone');
                dropZone.addEventListener('dragover', handleDragOver, false);
                dropZone.addEventListener('drop', handleFileSelect, false);

            } else {

                alert('The File APIs are not fully supported in this browser.');

            }

        </script>
    </body>
</html>