在看emWIN的時候看到一個圖片壓縮的算法可以有效的對二值圖(簡單的2中顏色或者更多)進行壓縮,壓縮的效果可以節省空間而且不丟失信息!
特點
缺點
RLE壓縮方式
/********************************************************************* * * bm0 */ static GUI_CONST_STORAGE unsigned char _ac0[] = { /* RLE: 011 Pixels @ 000,000 */ 11, 0xFF, /* ABS: 010 Pixels @ 011,000 */ 0, 10, 0xF6, 0xA8, 0x48, 0x1D, 0x11, 0x0E, 0x1E, 0x47, 0x8E, 0xE7, /* RLE: 021 Pixels @ 021,000 */ 21, 0xFF, /* ABS: 002 Pixels @ 010,001 */ 0, 2, 0xBF, 0x30, /* RLE: 008 Pixels @ 012,001 */ 8, 0x00, /* ABS: 003 Pixels @ 020,001 */ 0, 3, 0x17, 0x97, 0xFE, /* RLE: 017 Pixels @ 023,001 */ 17, 0xFF, /* ABS: 003 Pixels @ 008,002 */ 0, 3, 0xFE, 0x91, 0x08, /* RLE: 011 Pixels @ 011,002 */ 11, 0x00, /* ABS: 002 Pixels @ 022,002 */ 0, 2, 0x69, 0xF8, /* RLE: 016 Pixels @ 024,002 */ 16, 0xFF, /* RLE: 001 Pixels @ 008,003 */ 1, 0x7B, /* RLE: 005 Pixels @ 009,003 */ 5, 0x00, /* ABS: 004 Pixels @ 014,003 */ 0, 4, 0x03, 0x0E, 0x0F, 0x06, /* RLE: 005 Pixels @ 018,003 */ 5, 0x00, /* ABS: 002 Pixels @ 023,003 */ 0, 2, 0x49, 0xF6, /* RLE: 014 Pixels @ 025,003 */ 14, 0xFF, /* ABS: 013 Pixels @ 007,004 */ 0, 13, 0xBE, 0x04, 0x00, 0x00, 0x00, 0x03, 0x60, 0xC2, 0xED, 0xF1, 0xD3, 0x7F, 0x13, /* RLE: 004 Pixels @ 020,004 */ 4, 0x00, /* RLE: 001 Pixels @ 024,004 */ 1, 0x85, /* RLE: 014 Pixels @ 025,004 */ 14, 0xFF, /* ABS: 006 Pixels @ 007,005 */ 0, 6, 0x4A, 0x00, 0x00, 0x00, 0x06, 0xA2, /* RLE: 006 Pixels @ 013,005 */ 6, 0xFF, /* ABS: 007 Pixels @ 019,005 */ 0, 7, 0xCE, 0x21, 0x00, 0x00, 0x00, 0x19, 0xE4, /* RLE: 012 Pixels @ 026,005 */ 12, 0xFF, /* ABS: 006 Pixels @ 006,006 */ 0, 6, 0xD4, 0x08, 0x00, 0x00, 0x00, 0x7D, /* RLE: 008 Pixels @ 012,006 */ 8, 0xFF, /* ABS: 006 Pixels @ 020,006 */ 0, 6, 0xB8, 0x01, 0x00, 0x00, 0x00, 0x99, /* RLE: 012 Pixels @ 026,006 */ 12, 0xFF, /* ABS: 006 Pixels @ 006,007 */ 0, 6, 0x9B, 0x00, 0x00, 0x00, 0x15, 0xE5, /* RLE: 009 Pixels @ 012,007 */ 9, 0xFF, /* ABS: 005 Pixels @ 021,007 */ 0, 5, 0x48, 0x00, 0x00, 0x00, 0x5F, /* RLE: 012 Pixels @ 026,007 */ 12, 0xFF, /* ABS: 005 Pixels @ 006,008 */ 0, 5, 0x7A, 0x00, 0x00, 0x00, 0x46, /* RLE: 010 Pixels @ 011,008 */ 10, 0xFF, /* ABS: 006 Pixels @ 021,008 */ 0, 6, 0x82, 0x00, 0x00, 0x00, 0x3E, 0xFE, /* RLE: 011 Pixels @ 027,008 */ 11, 0xFF, /* ABS: 005 Pixels @ 006,009 */ 0, 5, 0x71, 0x00, 0x00, 0x00, 0x56, /* RLE: 010 Pixels @ 011,009 */ 10, 0xFF, /* ABS: 006 Pixels @ 021,009 */ 0, 6, 0x93, 0x00, 0x00, 0x00, 0x34, 0xFA, /* RLE: 011 Pixels @ 027,009 */ 11, 0xFF, /* ABS: 005 Pixels @ 006,010 */ 0, 5, 0x70, 0x00, 0x00, 0x00, 0x57, /* RLE: 010 Pixels @ 011,010 */ 10, 0xFF, /* ABS: 006 Pixels @ 021,010 */ 0, 6, 0x94, 0x00, 0x00, 0x00, 0x33, 0xFA, /* RLE: 011 Pixels @ 027,010 */ 11, 0xFF, /* ABS: 005 Pixels @ 006,011 */ 0, 5, 0x71, 0x00, 0x00, 0x00, 0x57, /* RLE: 010 Pixels @ 011,011 */ 10, 0xFF, /* ABS: 006 Pixels @ 021,011 */ 0, 6, 0x94, 0x00, 0x00, 0x00, 0x34, 0xFA, /* RLE: 011 Pixels @ 027,011 */ 11, 0xFF, /* ABS: 005 Pixels @ 006,012 */ 0, 5, 0x71, 0x00, 0x00, 0x00, 0x57, /* RLE: 010 Pixels @ 011,012 */ 10, 0xFF, /* ABS: 006 Pixels @ 021,012 */ 0, 6, 0x94, 0x00, 0x00, 0x00, 0x35, 0xFB, /* RLE: 008 Pixels @ 027,012 */ 8, 0xFF, /* ABS: 010 Pixels @ 003,013 */ 0, 10, 0xCF, 0x69, 0x59, 0x26, 0x00, 0x00, 0x00, 0x1D, 0x58, 0x57, /* RLE: 007 Pixels @ 013,013 */ 7, 0x56, /* ABS: 009 Pixels @ 020,013 */ 0, 9, 0x5B, 0x32, 0x00, 0x00, 0x00, 0x11, 0x53, 0x5E, 0xAF, /* RLE: 005 Pixels @ 029,013 */ 5, 0xFF, /* ABS: 002 Pixels @ 002,014 */ 0, 2, 0xD6, 0x18, /* RLE: 025 Pixels @ 004,014 */ 25, 0x00, /* RLE: 001 Pixels @ 029,014 */ 1, 0xA4, /* RLE: 004 Pixels @ 030,014 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,015 */ 1, 0x7E, /* RLE: 026 Pixels @ 003,015 */ 26, 0x00, /* RLE: 001 Pixels @ 029,015 */ 1, 0x46, /* RLE: 004 Pixels @ 030,015 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,016 */ 1, 0x63, /* RLE: 026 Pixels @ 003,016 */ 26, 0x00, /* RLE: 001 Pixels @ 029,016 */ 1, 0x34, /* RLE: 004 Pixels @ 030,016 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,017 */ 1, 0x64, /* RLE: 026 Pixels @ 003,017 */ 26, 0x00, /* RLE: 001 Pixels @ 029,017 */ 1, 0x35, /* RLE: 004 Pixels @ 030,017 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,018 */ 1, 0x64, /* RLE: 026 Pixels @ 003,018 */ 26, 0x00, /* RLE: 001 Pixels @ 029,018 */ 1, 0x35, /* RLE: 004 Pixels @ 030,018 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,019 */ 1, 0x64, /* RLE: 026 Pixels @ 003,019 */ 26, 0x00, /* RLE: 001 Pixels @ 029,019 */ 1, 0x35, /* RLE: 004 Pixels @ 030,019 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,020 */ 1, 0x64, /* RLE: 026 Pixels @ 003,020 */ 26, 0x00, /* RLE: 001 Pixels @ 029,020 */ 1, 0x35, /* RLE: 004 Pixels @ 030,020 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,021 */ 1, 0x64, /* RLE: 026 Pixels @ 003,021 */ 26, 0x00, /* RLE: 001 Pixels @ 029,021 */ 1, 0x35, /* RLE: 004 Pixels @ 030,021 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,022 */ 1, 0x64, /* RLE: 026 Pixels @ 003,022 */ 26, 0x00, /* RLE: 001 Pixels @ 029,022 */ 1, 0x35, /* RLE: 004 Pixels @ 030,022 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,023 */ 1, 0x64, /* RLE: 026 Pixels @ 003,023 */ 26, 0x00, /* RLE: 001 Pixels @ 029,023 */ 1, 0x35, /* RLE: 004 Pixels @ 030,023 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,024 */ 1, 0x64, /* RLE: 026 Pixels @ 003,024 */ 26, 0x00, /* RLE: 001 Pixels @ 029,024 */ 1, 0x35, /* RLE: 004 Pixels @ 030,024 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,025 */ 1, 0x64, /* RLE: 026 Pixels @ 003,025 */ 26, 0x00, /* RLE: 001 Pixels @ 029,025 */ 1, 0x35, /* RLE: 004 Pixels @ 030,025 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,026 */ 1, 0x64, /* RLE: 026 Pixels @ 003,026 */ 26, 0x00, /* RLE: 001 Pixels @ 029,026 */ 1, 0x35, /* RLE: 004 Pixels @ 030,026 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,027 */ 1, 0x64, /* RLE: 026 Pixels @ 003,027 */ 26, 0x00, /* RLE: 001 Pixels @ 029,027 */ 1, 0x35, /* RLE: 004 Pixels @ 030,027 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,028 */ 1, 0x62, /* RLE: 026 Pixels @ 003,028 */ 26, 0x00, /* RLE: 001 Pixels @ 029,028 */ 1, 0x34, /* RLE: 004 Pixels @ 030,028 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,029 */ 1, 0x6E, /* RLE: 026 Pixels @ 003,029 */ 26, 0x00, /* RLE: 001 Pixels @ 029,029 */ 1, 0x3B, /* RLE: 004 Pixels @ 030,029 */ 4, 0xFF, /* RLE: 001 Pixels @ 002,030 */ 1, 0xB5, /* RLE: 026 Pixels @ 003,030 */ 26, 0x00, /* RLE: 001 Pixels @ 029,030 */ 1, 0x7B, /* RLE: 005 Pixels @ 030,030 */ 5, 0xFF, /* ABS: 003 Pixels @ 003,031 */ 0, 3, 0x94, 0x1F, 0x0D, /* RLE: 020 Pixels @ 006,031 */ 20, 0x0F, /* ABS: 006 Pixels @ 026,031 */ 0, 6, 0x0D, 0x15, 0x6B, 0xF3, 0xFF, 0xFF, 0 }; // 416 bytes for 1024 pixels GUI_CONST_STORAGE GUI_BITMAP bm0 = { 32, // xSize 32, // ySize 32, // BytesPerLine GUI_COMPRESS_RLE8, // BitsPerPixel (unsigned char *)_ac0, // Pointer to picture data NULL, // Pointer to palette GUI_DRAW_RLEALPHA };
方法:
***************************************************************************************************************************************************
1.RLE概述
RLE(Run LengthEncoding行程編碼)算法是一個簡單高效的無損數據壓縮算法,其基本思路是把數據看成一個線性序列,而這些數據序列組織方式分成兩種情況:一種是連續的重復數據塊,另一種是連續的不重復數據塊。對於連續的重復數據快采用的壓縮策略是用一個字節(我們稱之為數據重數屬性)表示數據塊重復的次數,然后在這個數據重數屬性字節后面存儲對應的數據字節本身,例如某一個文件有如下的數據序列AAAAA,在未壓縮之前占用5個字節,而如果使用了壓縮之后就變成了5A,只占用兩個字節,對於連續不重復的數據序列,表示方法和連續的重復數據塊序列的表示方法一樣,只不過前面的數據重數屬性字節的內容為1。一般的這里的數據塊取一個字節,這篇文章中數據塊都默認為一個字節。為了更形象的說明RLE算法的原理我們給出最原始的RLE算法:
2. 原始RLE方法
給出的數據序列為:A-A-A-A-A-B-B-C-D
未壓縮前:A-A-A-A-A-B-B-C-D
(0x41-0x41-0x41-0x41-0x41-0x42-0x42-0x43-0x44)
壓縮后:5-A-2-B-1-C-1-D
(0x05-0x41-0x02-0x42-0x01-0x43-0x01-0x44)
從這里我們看到RLE的壓縮和解壓的算法原理非常簡單,實現起來也並不復雜。
在進行壓縮時逐個掃描待壓縮數據,如果碰到某個字節后有重復的字節則計數器就加1(計數器初始值默認為1,也可設置為0),直到找到一個不重復的字節為止,將當前計數器中的值存入數據重數屬性的字節中,將對應的該字節的數據存放在數據字節中,然后計數器置1繼續用同樣的策略掃描剩下的未壓縮文檔的數據。這里需要主意的是,數據重數屬性的單位是一個字節,故最大值為255,因此對於數據序列的某一個數據重復次數大於255時的數據,當計數器記到255時就必須強制將計數器值和當前數據值寫入數據重數屬性字節中和數據字節中,然后計數器置1繼續掃描,直到到達文件末尾。
由於壓縮后的數據格式為:數據重數,數據塊值,數據重數,數據塊值……,因此解壓的算法很簡單,就是讀出第一個位置上的數據重數字節存放的數據塊的個數N,然后將第二個位置上的數據字節存放的數據塊向解壓緩沖區寫N份,然后再讀出第三個位置上數據重數字節存放的數據塊的個數M,然后將第四個位置上數據字節存放的數據塊向解壓緩沖區寫M份,直到讀到壓縮數據末尾結束。
代碼如下:為了顯示更清楚(將數據重數屬性字節中的ASCII碼值轉換成了真實的數值,即本該為0x03的就用0x51表示)
- #include<stdio.h>
- #include<string.h>
- #include<malloc.h>
- #define MAX_SIZE 4096
- /**
- 實現了一個簡化版的RLE壓縮算法,該算法要求字符重復次數不超過79個(127-48)
- 如果是可打印的,則為78吧~ 因為127的ASCII碼對應的字符Del是不可打印的
- */
- void CompressRLE(char* input ,char* output)
- {
- int i,j,k = 0;
- for(i = 0; i < strlen(input); i++)
- {
- //直到找到和當前字符不相等的下個字符為止
- for(j = i;input[j] == input[i];j++)
- ;
- /*存放重復計數*/
- output[k++] = j - i + '0';
- /*存放重復的字符*/
- output[k++] = input[i];
- i = j - 1;
- }
- output[k] = '\0';
- }
- void UnCompressRLE(char* input ,char* output)
- {
- int i,j,count;
- for(i = 0; i < strlen(input);i = i + 2)
- {
- count = 0;/*當前重復字符計數*/
- /*在當前解壓字符串后存放原串剩余字符*/
- for( j = strlen(output); count < (input[i]-'0');j++)
- {
- output[j] = input[i+1];
- count ++;
- }
- }
- output[j] = '\0';
- }
- void main()
- {
- char* input = (char*)malloc(MAX_SIZE);
- char* output = (char*)malloc(2*MAX_SIZE);
- char* result = (char*)malloc(MAX_SIZE);
- memset(input,0,MAX_SIZE);
- memset(output,0,2*MAX_SIZE);
- memset(result,0,MAX_SIZE);
- printf("please input a string:\n");
- scanf("%s",input);
- printf("input = %s\n",input);
- CompressRLE(input,output);
- printf("output = %s\n",output);
- UnCompressRLE(output,result);
- printf("result = %s",result);
- }
3.RLE改進一(PCX文件的處理方法)
雖然上述算法實現起來很容易,但是它的一個明顯的缺點就是如果要壓縮的數據很少有連續重復的數據塊,采用上訴壓縮算法之后數據量反而會“膨脹”,最壞情況下就是所有的數據都不連續重復,則壓縮后的數據量會增大一倍。因此就提出了一種改進的RLE算法。算法思路和原始RLE算法大致相當,對於重復的連續數據塊依然采用上述算法,不同的是對於不重復的數據塊,不會再在它前面插入標志重復次數的字節了,而是直接將該不重復的數據塊寫入壓縮文件。這樣即使原文件所有的數據都不是連續重復出現的,壓縮后的文件也不會膨脹。但是這樣簡單的邏輯顯然是不行的,這里的一個問題就是在解壓縮的時候,如何判斷一個字節是原始數據字節還是數據重數字節標志。為了解決這個問題,我們將數據重數字節的高兩位置1,用字節剩下的六位來表示(此時能表示的最大長度為63)數據重數,這樣在解壓的時候如果遇到的字節高兩位不全為1則一定是數據字節,但是這里還有一個問題是,如果遇見的字節的高兩位全1是就不能判斷是數據重數字節還是數據字節,因此,光處理數據重數屬性字節的高位是不行的,實現的辦法只能是,在數據壓縮的時候如果發現數據的高兩位是1只能再在該數據字節前面插入數據重數字節,置其值為1(此時由於高兩位作為標記位已經置為1,故這里實際存儲的是0XC1)。這樣這個改進的RLE算法就完成了,其平均壓縮率也比原始RLE算法更高,但是相對的壓縮速度就稍微慢了。同樣的與原始RLE算法一樣也有最大長度問題,處理方法與原始RLE一樣。
下面是RLE改進一的形象舉例說明:給出的數據序列為:A-A-A-A-A-B-B-C-D
未壓縮前:A-A-A-A-A-B-B-C-D
(0x41-0x41-0x41-0x41-0x41-0x42-0x42-0x43-0x44)
壓縮后:5-A-2-B-C-D
(0xC5-0x41-0xC2-0x42-0x43-0x44)
該算法的解壓算法很簡單,就是將讀取器指針指向第一個字節,逐個讀取每個字節,判斷該字節高兩位是否全為1,如果是,則表示該字節是數據重數字節,則從該字節后六位讀取重復次數N,然后將下一個字節向解壓緩沖區寫入N份,令讀取器加指針加2接着讀取下面的字節,如果該字節高兩位不全為1,則表明該字節是數據字節,故將該字節直接寫入解壓緩沖區,並使讀取器指針加1接着讀取下面的字節,直到讀取器指針達到文件末尾為止。
4.RLE改進二
上述優化后的RLE算法,在原始數據普遍大於192(0xC0)的情況下,其優化效果相對於優化前的算法沒有明顯改善。原因在於,原始的RLE算法和改進后的RLE算法對於連續出現的不重復數據的處理方式都是一個一個處理的,沒有把不重復數據作為一個整體進行處理。RLE改進二算法的優化思想就是對連續的不重復數據的處理和連續重復數據的處理統一起來,不論數據是否連續重復,都在之前設置一個數據重數字節,這樣在解壓的時候就不需要區分是數據重數字節還是原始數據字節了。唯一需要區分的是后面跟的數據類型。區分的方法就是數據重數字節的最高位,如果最高位是1則表示后面跟的是連續重復的數據,如果最高位是0則表示后面跟的是連續不重復的數據。標志字節的低7位存儲數值(最大值是127),對於連續重復的數據,這個數字表示需要重復的次數,對於連續不重復數據,這個數字表示連續不重復數據塊的長度。需要注意的是,只有重復次數超過2的數據才被認為是連續重復數據,因為如果數據的重復次數是2,壓縮后加上標志字節后總的長度沒有變化,因此沒有必要處理。
下面是該RLE改進二的形象舉例說明:給出的數據序列為:A-A-A-A-A-B-B-C-D
未壓縮前:A-A-A-A-A-B-B-C-D
(0x41-0x41-0x41-0x41-0x41-0x42-0x42-0x43-0x44)
壓縮后:5-A-4-B-B-C-D
該RLE改進二的算法的壓縮算法設計如下:掃描指針從數據開始處讀取數據,並依次向后掃描各個字節,每向后讀取一個字節計數器加1,在掃描的同時比較每個字節是否相同,這樣就會有兩種情況:一種情況是掃描過程中前三個字節出現的都是連續重復的字節,則將計數器最高位置1,然后繼續掃描,直到出現1個不相同的字節,此時令掃描指針回退1個字節,計數器減1,然后將計數器的值寫入數據重數字節中,並將當前掃描指針指向字節寫到隨后的數據字節中。另一種情況是掃描過程中前兩個字節就不相同,則將計數器最高位置0,然后接着向后掃描,若在此后的掃描過程中出現連續3個相同的字節,則停止掃描,並令掃描指針回退到初始值,計數器減3,然后將當前計數器的值寫入數據重數字節中,並將當前掃描指針指向字節及其后N個字節寫到隨后的數據字節中。
RLE改進二的解壓縮算法很簡單,因為壓縮后的格式是:數據重數,數據塊(組),數據重數,數據塊(組)……,因此從頭掃描,取出數據重數字節並判斷其標志位,若為1,則將接下來的一個字節向解壓緩沖區寫N份(這里的N是由數據重數字節低七位計算出來的),然后掃描指針加2,接着向后掃描。若為0,則將數據重數字節后面的N個字節都寫入解壓緩沖區,然后掃描指針加N+1,接着向后掃描。整個解壓過程直到掃描指針獨到整個數據尾部為止。
5.RLE改進三
以上算法中塊數屬性占用一個字節,原始RLE算法能表示的最大的數據重數為MAX=255,RLE改進一中由於高兩位做了標記位,則能表示的最大數據重數為MAX=63,RLE改進二中由於高一位做了標記位,則能表示的最大數據重數為MAX=127,所以如果某數據塊重復次數超過了最大值MAX,則按以上各算法,該連續重數的數據塊只能被分割成多個(數據重數,數據塊)對來表示,這樣就加大了空間的開銷。
針對上訴算法中出現的連續重復數據塊數超過MAX值的情況,規定如果數據重數字節的值為FF則在其后的一個字節仍為數據重數字節,最終的值為這幾個數據重數字節值的和,如果特殊的,某數據塊連續重復數正好是MAX值,則在該數據塊對應的數據重數字節F后再添加一個數據重數字節置其值為0x00。