Notepad ++用于图标的位图格式是什么(所以我可以更改它们)?

时间:2012-12-12 00:21:28

标签: bitmap customization notepad++ file-format

我正在使用Notepad ++来完成我目前的大部分编码工作。但是,我想改变它的图标,只是为了定制而已。

所以我做的是用Resource Hacker打开.exe,看看图标在哪里。它们似乎是位图格式,这很奇怪,因为它们具有透明性,并且用我知道的任何“位图”(BM标题,“Windows位图”)格式替换它们不起作用(图标不是'显示)。

所以我想问一下,这些文件的格式是什么,以及如何制作它们?

位图示例可在Notepad++ repository中找到:
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 closeFile 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

正如您所看到的,它们具有灰色遮罩,这使我认为Alpha通道以某种非标准方式存储在格式中。 (或者,它具有二进制透明度,#C0C0C0表示透明)。

它也相当大,每个像素表示超过5个字节(!)。

这就是上面第一个位图的标题:

Hex Workshop

有什么想法吗?只知道这是什么格式就足够了。

2 个答案:

答案 0 :(得分:3)

这是8BPP(每像素8位)索引位图。在“BM”之后,有一个52字节的标题,256色的调色板和索引的像素数据。每种颜色都是4字节,采用BGR_格式(不使用第四个字节;如果是alpha通道,则会看到FF而不是00)。

索引像素数据从0x436开始。这很简单:一个字节代表调色板中的一个索引。例如,前几个字节是07 07 18 18. 0x07将使该像素使用调色板中的第8种颜色,即#C0C0C0;并且0x18将使该像素使用调色板中的第25种颜色,即#CECECE。

我猜测透明度是按照你的猜测来处理的。无论透明像素在哪里,我都会看到07,调色板中相应的颜色是#C0C0C0。

请注意,像素数据是颠倒存储的。也就是说,偏移量0x436处的前16个字节代表 bottom 像素行。

这里有更详细的解释:http://en.wikipedia.org/wiki/BMP_file_format#File_structure

编辑:至于如何制作它们,只需询问您喜欢的图像编辑软件,将图像导出为256色位图(或8BPP位图,无论哪个可用)。请注意,如果在绘制后保存颜色,Microsoft Paint将会破坏颜色,因此在实际绘制任何内容之前保存它。

答案 1 :(得分:0)

Jeff指出的所有内容都是正确的,并将继续作为公认的答案。但是,如果其他人可能想要调整NP ++,我已经制作了一个小转换器,您可以使用它将任何8位索引位图转换为可以与NP ++一起使用的位图(通过资源黑客或类似方法)。

它是用Javascript编写的,只在Chrome上测试过,代码非常草率:JsFiddle

要使用它,请在您喜欢的程序中创建一个8位16x16索引位图,然后将其拖放到该内容中。它将输出一些调试细节并为您提供一个链接,以便您可以下载"兼容的位图(但所有这些都发生在HTML5的客户端)。

编辑:SO抱怨我在没有发布相关代码的情况下给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>