予熱
前のPNGファイル形式[詳細] PNGファイル形式の詳細。
PNG画像フォーマットファイル署名ドメインおよびPNGファイル(IHDR、IDAT、IEND)組成物の8バイトの三つ以上の後続のブロック。
PNGファイル署名ファイルは、PNG形式の8バイト(89 50 4E 47 0D 0A 1Aは0A 16進数)を含みます。
データブロックは、4つの主要なデータ・ブロックがあります。
- ヘッダデータブロックIHDRは、(ヘッダチャンク):画像の基本的な情報が含まれ、それは、第1のデータブロックとして表示され、一度だけ。
- パレットデータブロックPLTE(パレットチャンク):画像データのブロックの前に配置されなければなりません。
- 画像データブロックIDAT(画像データチャンク):実際の画像データを記憶します。PNGデータは、連続的な画像データの複数のブロックを含有することができます。
- エンド・画像データIEND(画像トレーラーチャンク):ファイルの最後に、PNGデータストリームの終わりを示します。
おそらく、このように、一緒にすべてをブロックします:
PNG識別子 | PNGデータブロック(IHDR) | PNGデータブロック(データブロックの他のタイプ) | ... | データブロックのPNG端(IEND) |
---|
目標のグラフ:
トゥルーカラーのPNG画像を生成します
PNG画像トゥルーカラーパレットデータブロックはPLTEは、IDATデータブロックは、RGBの画像データに格納されている必要はありません。
分析 - トゥルーカラーPNG IDATデータブロック
例に7×7解像度:
コード - データ・ブロックTFT PNG IDATを生成します
// 彩虹的七种颜色
uint32_t rainbowColors[] = {
0XFF0000, // 红
0XFFA500, // 橙
0XFFFF00, // 黄
0X00FF00, // 绿
0X007FFF, // 青
0X0000FF, // 蓝
0X8B00FF // 紫
};
// 生成真彩 PNG 图片的图像数据块 IDAT
void genRGB24Data(uint8_t *rgbData, int width, int height) {
for (int i = 0; i < height; ++i) {
// 当前颜色
uint32_t currentColor = rainbowColors[0];
if(i < 100) {
currentColor = rainbowColors[0];
} else if(i < 200) {
currentColor = rainbowColors[1];
} else if(i < 300) {
currentColor = rainbowColors[2];
} else if(i < 400) {
currentColor = rainbowColors[3];
} else if(i < 500) {
currentColor = rainbowColors[4];
} else if(i < 600) {
currentColor = rainbowColors[5];
} else if(i < 700) {
currentColor = rainbowColors[6];
}
// 当前颜色 R 分量
uint8_t R = (currentColor & 0xFF0000) >> 16;
// 当前颜色 G 分量
uint8_t G = (currentColor & 0x00FF00) >> 8;
// 当前颜色 B 分量
uint8_t B = currentColor & 0x0000FF;
// 每个扫描行前第一个字节是过滤器类型
rgbData[3*(i*width)+i] = 0x00;
for (int j = 0; j < width; ++j) {
int currentIndex = 3*(i*width+j)+(i+1);
rgbData[currentIndex] = R;
rgbData[currentIndex+1] = G;
rgbData[currentIndex+2] = B;
}
}
}
トゥルーカラーPNG完全なコード生成
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "zlib.h"
// ***** functions in util.c *****
bool isBigEndianOrder();
void genRGB24Data(uint8_t *rgbData, int width, int height);
uint32_t switchUint32(uint32_t i);
uint32_t calcCrc32(uint32_t dataASCII, uint8_t *data, uint32_t length);
typedef struct {
uint32_t width;
uint32_t height;
uint8_t bitDepth;
uint8_t colorType;
uint8_t compressionMethod;
uint8_t filterMethod;
uint8_t interlaceMethod;
} PNG_IHDR_DATA;
int main() {
// PNG 图片尺寸
int width = 700, height = 700;
// IDAT 中数据部分长度
uint32_t IDAT_RGB_DATA_LENGTH = width*height*3+height;
// PNG 文件包括 8 字节文件署名(89 50 4E 47 0D 0A 1A 0A,十六进制),用来识别 PNG 格式。
uint8_t PNG_FILE_SIGNATURE[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
// IHDR 每个字母对应的 ASCII
uint32_t IHDR_ASCII = switchUint32(0x49484452);
// IDAT 每个字母对应的ASCII
uint32_t IDAT_ASCII = switchUint32(0x49444154);
// PNG 文件的结尾 12 个字节看起来总应该是这样的:(00 00 00 00 49 45 4E 44 AE 42 60 82,十六进制)
uint8_t PNG_IEND_DATA[] = {0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82};
FILE *file = fopen("/Users/staff/Desktop/0-true-color.png", "wb");
// FILE *file = fopen("C:\\Users\\Administrator\\Desktop\\0-true-color.png", "wb+");
if (!file) {
printf("Could not write file\n");
return -1;
}
// 真彩 PNG 图片 存储的是 RGB 数
uint8_t *rgb24Data = (uint8_t *)malloc(IDAT_RGB_DATA_LENGTH);
// 填充 IDAT 的 RGB 数据
genRGB24Data(rgb24Data, width, height);
// 写 PNG 文件署名
fwrite(PNG_FILE_SIGNATURE, 1, sizeof(PNG_FILE_SIGNATURE), file);
// 准备 IHDR 数据
PNG_IHDR_DATA pngIhdrData;
pngIhdrData.width = switchUint32(width);
pngIhdrData.height = switchUint32(height);
pngIhdrData.bitDepth = 8;
pngIhdrData.colorType = 2;// 2:真彩色图像,8或16 6:带α通道数据的真彩色图像,8或16
pngIhdrData.compressionMethod = 0;
pngIhdrData.filterMethod = 0;
pngIhdrData.interlaceMethod = 0;
// IHDR 数据长度
uint32_t IHDR_DATA_LENGTH = 13;
// IHDR 数据长度 转换成大端字节序
uint32_t pngIhdrDataSize = switchUint32(IHDR_DATA_LENGTH);
// 计算 IHDR CRC32
uint32_t ihdrDataCrc32 = calcCrc32(IHDR_ASCII, (uint8_t *) &pngIhdrData, IHDR_DATA_LENGTH);
// 写 IHDR 数据长度
fwrite(&pngIhdrDataSize, 1, sizeof(pngIhdrDataSize), file);
// 写 IHDR ASCII
fwrite(&IHDR_ASCII, 1, sizeof(IHDR_ASCII), file);
// 写 IHDR 数据
fwrite(&pngIhdrData, 1, IHDR_DATA_LENGTH, file);
// 写 IHDR CRC32
fwrite(&ihdrDataCrc32, 1, sizeof(ihdrDataCrc32), file);
// zlib 压缩数据
uint8_t buf[IDAT_RGB_DATA_LENGTH];
// 压缩后 buf 的数据长度 压缩完成后就是实际大小了
uint32_t buflen = IDAT_RGB_DATA_LENGTH;
// 执行 zlib 的压缩方法
compress(buf, (uLongf *) &buflen, rgb24Data, IDAT_RGB_DATA_LENGTH);
printf("\n压缩前数据长度:%d \n压缩后数据长度为:%d \n", IDAT_RGB_DATA_LENGTH, buflen);
// 计算 IDAT CRC32
uint32_t idatDataCrc32 = calcCrc32(IDAT_ASCII, buf, buflen);
// IDAT 数据长度 转换成大端字节序
uint32_t tmpBuflen = switchUint32(buflen);
// 写 IDAT 数据长度
fwrite(&tmpBuflen, 1, sizeof(tmpBuflen), file);
// 写 IDAT ASCII
fwrite(&IDAT_ASCII, 1, sizeof(IDAT_ASCII), file);
// 写 IDAT 数据
fwrite(buf, 1, buflen, file);
// 写 IDAT CRC32
fwrite(&idatDataCrc32, 1, sizeof(idatDataCrc32), file);
// 写 IEND 信息
fwrite(PNG_IEND_DATA, 1, sizeof(PNG_IEND_DATA), file);
// 查看字节序
if(isBigEndianOrder()) {
printf("大端字节序");
} else {
printf("小端字节序");
}
// 收尾工作
fflush(file);
free(rgb24Data);
fclose(file);
return 0;
}
PNG画像生成指数
インデックスPNGイメージはPLTEパレットデータブロックは、IDATデータブロックがPLTEパレットカラーインデックス画像データに格納されている必要があります。
分析 - PNG IDATデータブロックインデックス
例に7×7解像度:
コード - 生成されたインデックスパレットPNG PLTE
// 彩虹的七种颜色
uint32_t rainbowColors[] = {
0XFF0000, // 红
0XFFA500, // 橙
0XFFFF00, // 黄
0X00FF00, // 绿
0X007FFF, // 青
0X0000FF, // 蓝
0X8B00FF // 紫
};
/**
* 生成索引 PNG 图片的调色板 PLTE
* @param rgbPLTEData
*/
void genRGBPLTE(uint8_t *rgbPLTEData) {
for (int i = 0; i < 7; ++i) {
uint32_t currentColor = rainbowColors[i];
// 当前颜色 R 分量
uint8_t R = (currentColor & 0xFF0000) >> 16;
// 当前颜色 G 分量
uint8_t G = (currentColor & 0x00FF00) >> 8;
// 当前颜色 B 分量
uint8_t B = currentColor & 0x0000FF;
int currentIndex = 3*i;
rgbPLTEData[currentIndex] = R;
rgbPLTEData[currentIndex+1] = G;
rgbPLTEData[currentIndex+2] = B;
}
}
コード - インデックスデータブロックPNG IDATを生成します
/**
* 生成索引 PNG 图片的图像数据块 IDAT
* @param rgbIndexData
* @param width
* @param height
*/
void genRGBIndexData(uint8_t *rgbIndexData, int width, int height) {
for (int i = 0; i < height; ++i) {
uint8_t currentColorIndex = 0;
if(i < 100) {
currentColorIndex = 0;
} else if(i < 200) {
currentColorIndex = 1;
} else if(i < 300) {
currentColorIndex = 2;
} else if(i < 400) {
currentColorIndex = 3;
} else if(i < 500) {
currentColorIndex = 4;
} else if(i < 600) {
currentColorIndex = 5;
} else if(i < 700) {
currentColorIndex = 6;
}
// 每个扫描行前第一个字节是过滤器类型
rgbIndexData[(i*width)/2+i] = 0x00;
for (int j = 0; j < width; ++j) {
int currentIndex = (i*width+j)/2+(i+1);
int positionInByte = j%2;
if(positionInByte == 0) {
rgbIndexData[currentIndex] = currentColorIndex << 4;
} else {
rgbIndexData[currentIndex] += currentColorIndex;
}
}
}
}
PNG、完全なコード生成指数
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include "zlib.h"
// ***** functions in util.c *****
bool isBigEndianOrder();
void genRGBPLTE(uint8_t *rgbData);
void genRGBIndexData(uint8_t *rgbIndexData, int width, int height);
uint32_t switchUint32(uint32_t i);
uint32_t calcCrc32(uint32_t dataASCII, uint8_t *data, uint32_t length);
typedef struct {
uint32_t width;
uint32_t height;
uint8_t bitDepth;
uint8_t colorType;
uint8_t compressionMethod;
uint8_t filterMethod;
uint8_t interlaceMethod;
} PNG_IHDR_DATA;
int main() {
// PNG 图片尺寸
int width = 700, height = 700;
// IDAT 中数据部分长度
uint32_t IDAT_INDEX_DATA_LENGTH = width*height/2+height;
// PNG 文件包括 8 字节文件署名(89 50 4E 47 0D 0A 1A 0A,十六进制),用来识别 PNG 格式。
uint8_t PNG_FILE_SIGNATURE[] = {0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A};
// IHDR 每个字母对应的 ASCII
uint32_t IHDR_ASCII = switchUint32(0x49484452);
// PLTE 每个字母对应的ASCII
uint32_t PLTE_ASCII = switchUint32(0x504C5445);
// IDAT 每个字母对应的ASCII
uint32_t IDAT_ASCII = switchUint32(0x49444154);
// PNG 文件的结尾 12 个字节看起来总应该是这样的:(00 00 00 00 49 45 4E 44 AE 42 60 82,十六进制)
uint8_t PNG_IEND_DATA[] = {0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4E, 0x44, 0xAE, 0x42, 0x60, 0x82};
FILE *file = fopen("/Users/staff/Desktop/0-indexed-color.png", "wb");
// FILE *file = fopen("C:\\Users\\Administrator\\Desktop\\0-indexed-color.png", "wb+");
if (!file) {
printf("Could not write file\n");
return -1;
}
// 红橙黄绿青蓝紫-七种颜色的调色板 7 种颜色 * 每种颜色占 3 字节
uint8_t *rgbPLTEData = (uint8_t *)malloc(7*3);
// 索引 PNG 图片,IDAT 存储的是 PLTE 中的图片索引
uint8_t *rgbIndexData = (uint8_t *)malloc(IDAT_INDEX_DATA_LENGTH);
// 填充 红橙黄绿青蓝紫-七种颜色的调色板
genRGBPLTE(rgbPLTEData);
// 填充 IDAT 的 PLTE 索引
genRGBIndexData(rgbIndexData, width, height);
// 写 PNG 文件署名
fwrite(PNG_FILE_SIGNATURE, 1, sizeof(PNG_FILE_SIGNATURE), file);
// 准备 IHDR 数据
PNG_IHDR_DATA pngIhdrData;
pngIhdrData.width = switchUint32(width);
pngIhdrData.height = switchUint32(height);
pngIhdrData.bitDepth = 4;
pngIhdrData.colorType = 3; // 3:索引彩色图像,1,2,4或8
pngIhdrData.compressionMethod = 0;
pngIhdrData.filterMethod = 0;
pngIhdrData.interlaceMethod = 0;
// IHDR 数据长度
uint32_t IHDR_DATA_LENGTH = 13;
// IHDR 数据长度 转换成大端字节序
uint32_t pngIhdrDataSize = switchUint32(IHDR_DATA_LENGTH);
// 计算 IHDR CRC32
uint32_t ihdrDataCrc32 = calcCrc32(IHDR_ASCII, (uint8_t *) &pngIhdrData, IHDR_DATA_LENGTH);
// 写 IHDR 数据长度
fwrite(&pngIhdrDataSize, 1, sizeof(pngIhdrDataSize), file);
// 写 IHDR ASCII
fwrite(&IHDR_ASCII, 1, sizeof(IHDR_ASCII), file);
// 写 IHDR 数据
fwrite(&pngIhdrData, 1, IHDR_DATA_LENGTH, file);
// 写 IHDR CRC32
fwrite(&ihdrDataCrc32, 1, sizeof(ihdrDataCrc32), file);
// 准备 PLTE 调色板信息
// PLTE 数据长度
uint32_t PLTE_DATA_LENGTH = 21;
// PLTE 数据长度 转换成大端字节序
uint32_t pngPlteDataLength = switchUint32(PLTE_DATA_LENGTH);
// 计算 PLTE CRC32
uint32_t plteDataCrc32 = calcCrc32(PLTE_ASCII, rgbPLTEData, PLTE_DATA_LENGTH);
// 写 PLTE 数据长度
fwrite(&pngPlteDataLength, 1, sizeof(pngPlteDataLength), file);
// 写 PLTE ASCII
fwrite(&PLTE_ASCII, 1, sizeof(PLTE_ASCII), file);
// 写 PLTE 数据
fwrite(rgbPLTEData, 1, PLTE_DATA_LENGTH, file);
// 写 PLTE CRC32
fwrite(&plteDataCrc32, 1, sizeof(plteDataCrc32), file);
// zlib 压缩数据
// buf 用于存放压缩后的数据
uint8_t buf[IDAT_INDEX_DATA_LENGTH];
// 压缩后 buf 的数据长度 压缩完成后就是实际大小了
uint32_t buflen = IDAT_INDEX_DATA_LENGTH;
// 执行 zlib 的压缩方法
compress(buf, (uLongf *) &buflen, rgbIndexData, IDAT_INDEX_DATA_LENGTH);
printf("\n压缩前数据长度:%d \n压缩后数据长度为:%d \n", IDAT_INDEX_DATA_LENGTH, buflen);
// 计算 IDAT CRC32
uint32_t idatDataCrc32 = calcCrc32(IDAT_ASCII, buf, buflen);
// IDAT 数据长度 转换成大端字节序
uint32_t tmpBuflen = switchUint32(buflen);
// 写 IDAT 数据长度
fwrite(&tmpBuflen, 1, sizeof(tmpBuflen), file);
// 写 IDAT ASCII
fwrite(&IDAT_ASCII, 1, sizeof(IDAT_ASCII), file);
// 写 IDAT 数据
fwrite(buf, 1, buflen, file);
// 写 IDAT CRC32
fwrite(&idatDataCrc32, 1, sizeof(idatDataCrc32), file);
// 写 IEND 信息
fwrite(PNG_IEND_DATA, 1, sizeof(PNG_IEND_DATA), file);
// 查看字节序
if(isBigEndianOrder()) {
printf("大端字节序");
} else {
printf("小端字节序");
}
// 收尾工作
fflush(file);
free(rgbPLTEData);
free(rgbIndexData);
fclose(file);
return 0;
}
概要&ビュー
トゥルーカラーPNG、PNG画像のインデックスの差分を生成:
- カラータイプのIHDRヘッダ・データ・ブロック、色タイプがPNGインデックス付けされ
3:索引彩色图像
、PNGカラーTFT方式2:真彩色图像
。 - PLTEパレットデータブロックは、インデックスPNG、PNG真のカラーパレットを必要はありませんがパレットなければなりません。
- インデックスはPNGカラーパレットインデックスに格納されたデータブロックに格納されているIDATデータは、トゥルーカラーRGBデータは、PNGに格納されています。
手作りのPNG画像を見てみましょう:
おめでとうございます!
コード:
11-RGBツーPNG
参考文献:
ポータブルネットワークグラフィックス(PNG)仕様と拡張機能
コンテンツが正しくありませんか?著者にお問い合わせください:
ブログ記事複数のプラットフォームからこの記事OpenWriteリリース!