Mercurial > hg > dmlib
comparison src/libgfx.c @ 1279:0d3f5f44c0c4
Somewhat improve PCX read support in libgfx.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Fri, 18 Aug 2017 02:03:15 +0300 |
parents | 4e074b9b4789 |
children | 300a51e98fc3 |
comparison
equal
deleted
inserted
replaced
1278:5206e3d4e6b7 | 1279:0d3f5f44c0c4 |
---|---|
679 return res; | 679 return res; |
680 } | 680 } |
681 #endif | 681 #endif |
682 | 682 |
683 | 683 |
684 #define DMPCX_PAL_COLORS 16 // Number of internal palette colors | |
685 | |
684 typedef struct | 686 typedef struct |
685 { | 687 { |
686 Uint8 r,g,b; | 688 Uint8 r,g,b; |
687 } DMPCXColor; | 689 } DMPCXColor; |
688 | 690 |
689 | 691 |
690 typedef struct | 692 typedef struct |
691 { | 693 { |
692 Uint8 manufacturer, | 694 Uint8 manufacturer, // always 0x0a |
693 version, | 695 version, // Z-Soft PCX Paintbrush version: |
694 encoding, | 696 // 0 = v2.5, 2 = v2.8 with palette, 3 = v2.8 without palette, 5 = v3.0 or better |
695 bpp; | 697 encoding, // usually 0x01 = RLE, 0x00 = uncompressed |
698 bitsPerPlane; // bits per pixel per plane | |
699 | |
696 Uint16 xmin, ymin, xmax, ymax; | 700 Uint16 xmin, ymin, xmax, ymax; |
697 Uint16 hres, vres; | 701 Uint16 hres, vres; // resolution in DPI, can be image dimensions as well. |
698 DMPCXColor colormap[16]; | 702 DMPCXColor colorMap[DMPCX_PAL_COLORS]; |
699 Uint8 reserved; | 703 Uint8 reserved; // should be set to 0 |
700 Uint8 nplanes; | 704 Uint8 nplanes; // number of planes |
701 Uint16 bpl; | 705 Uint16 bpl; // bytes per plane LINE |
702 Uint16 palinfo; | 706 Uint16 palInfo; // 1 = color/BW, 2 = grayscale |
703 Uint8 filler[58]; | 707 Uint16 hScreenSize, vScreenSize; |
708 Uint8 filler[54]; | |
704 } DMPCXHeader; | 709 } DMPCXHeader; |
705 | 710 |
706 | 711 |
707 typedef struct | 712 typedef struct |
708 { | 713 { |
740 } | 745 } |
741 | 746 |
742 static int dmWritePCXRow(void *cbdata, Uint8 *row, size_t len) | 747 static int dmWritePCXRow(void *cbdata, Uint8 *row, size_t len) |
743 { | 748 { |
744 DMPCXData *pcx = (DMPCXData *) cbdata; | 749 DMPCXData *pcx = (DMPCXData *) cbdata; |
745 int plane; | |
746 size_t soffs = 0; | 750 size_t soffs = 0; |
747 | 751 |
748 // fprintf(stderr, "%d, %d * %d = %d\n", len, pcx->header->bpl, pcx->header->nplanes, pcx->header->nplanes * pcx->header->bpl); | 752 // fprintf(stderr, "%d, %d * %d = %d\n", len, pcx->header->bpl, pcx->header->nplanes, pcx->header->nplanes * pcx->header->bpl); |
749 | 753 |
750 pcx->bufOffs = 0; | 754 pcx->bufOffs = 0; |
751 | 755 |
752 for (plane = 0; plane < pcx->header->nplanes; plane++) | 756 for (int plane = 0; plane < pcx->header->nplanes; plane++) |
753 { | 757 { |
754 Uint8 data = dmPCXGetByte(row, len, soffs++), | 758 Uint8 data = dmPCXGetByte(row, len, soffs++), |
755 count = 1; | 759 count = 1; |
756 | 760 |
757 // size_t blen = pcx->header->bpl * pcx->header->nplanes; | 761 // size_t blen = pcx->header->bpl * pcx->header->nplanes; |
812 | 816 |
813 // Create PCX header | 817 // Create PCX header |
814 dmMemset(&hdr, 0, sizeof(hdr)); | 818 dmMemset(&hdr, 0, sizeof(hdr)); |
815 if (spec->paletted) | 819 if (spec->paletted) |
816 { | 820 { |
817 int i; | 821 for (int i = 0; i < (img->ncolors > DMPCX_PAL_COLORS ? DMPCX_PAL_COLORS : img->ncolors); i++) |
818 for (i = 0; i < (img->ncolors > 16 ? 16 : img->ncolors); i++) | 822 { |
819 { | 823 hdr.colorMap[i].r = img->pal[i].r; |
820 hdr.colormap[i].r = img->pal[i].r; | 824 hdr.colorMap[i].g = img->pal[i].g; |
821 hdr.colormap[i].g = img->pal[i].g; | 825 hdr.colorMap[i].b = img->pal[i].b; |
822 hdr.colormap[i].b = img->pal[i].b; | |
823 } | 826 } |
824 } | 827 } |
825 hdr.manufacturer = 10; | 828 hdr.manufacturer = 10; |
826 hdr.version = 5; | 829 hdr.version = 5; |
827 hdr.encoding = 1; | 830 hdr.encoding = 1; |
828 hdr.bpp = 8; | 831 hdr.bitsPerPlane = 8; |
829 hdr.hres = img->width * spec->scaleX; | 832 hdr.hres = img->width * spec->scaleX; |
830 hdr.vres = img->height * spec->scaleY; | 833 hdr.vres = img->height * spec->scaleY; |
831 hdr.xmin = hdr.ymin = 0; | 834 hdr.xmin = hdr.ymin = 0; |
832 hdr.xmax = hdr.hres - 1; | 835 hdr.xmax = hdr.hres - 1; |
833 hdr.ymax = hdr.vres - 1; | 836 hdr.ymax = hdr.vres - 1; |
834 hdr.nplanes = dmImageGetBytesPerPixel(pcx.format); | 837 hdr.nplanes = dmImageGetBytesPerPixel(pcx.format); |
835 hdr.palinfo = 1; | 838 hdr.palInfo = 1; |
836 | 839 |
837 res = (img->width * spec->scaleX); | 840 res = (img->width * spec->scaleX); |
838 hdr.bpl = res / 2; | 841 hdr.bpl = res / 2; |
839 if (res % 2) hdr.bpl++; | 842 if (res % 2) hdr.bpl++; |
840 hdr.bpl *= 2; | 843 hdr.bpl *= 2; |
841 | 844 |
842 dmMsg(3, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n", | 845 dmMsg(3, "PCX: paletted=%d, nplanes=%d, bpp=%d, bpl=%d\n", |
843 spec->paletted, hdr.nplanes, hdr.bpp, hdr.bpl); | 846 spec->paletted, hdr.nplanes, hdr.bitsPerPlane, hdr.bpl); |
844 | 847 |
845 pcx.bufLen = hdr.bpl * 4; | 848 pcx.bufLen = hdr.bpl * 4; |
846 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) | 849 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) |
847 { | 850 { |
848 res = dmError(DMERR_MALLOC, | 851 res = dmError(DMERR_MALLOC, |
853 | 856 |
854 // Write PCX header | 857 // Write PCX header |
855 if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) || | 858 if (!dm_fwrite_byte(pcx.fp, hdr.manufacturer) || |
856 !dm_fwrite_byte(pcx.fp, hdr.version) || | 859 !dm_fwrite_byte(pcx.fp, hdr.version) || |
857 !dm_fwrite_byte(pcx.fp, hdr.encoding) || | 860 !dm_fwrite_byte(pcx.fp, hdr.encoding) || |
858 !dm_fwrite_byte(pcx.fp, hdr.bpp)) | 861 !dm_fwrite_byte(pcx.fp, hdr.bitsPerPlane)) |
859 { | 862 { |
860 res = dmError(DMERR_FWRITE, | 863 res = dmError(DMERR_FWRITE, |
861 "PCX: Could not write basic header data.\n"); | 864 "PCX: Could not write basic header data.\n"); |
862 goto error; | 865 goto error; |
863 } | 866 } |
872 res = dmError(DMERR_FWRITE, | 875 res = dmError(DMERR_FWRITE, |
873 "PCX: Could not write image dimensions.\n"); | 876 "PCX: Could not write image dimensions.\n"); |
874 goto error; | 877 goto error; |
875 } | 878 } |
876 | 879 |
877 if (!dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap))) | 880 if (!dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.colorMap, sizeof(hdr.colorMap))) |
878 { | 881 { |
879 res = dmError(DMERR_FWRITE, | 882 res = dmError(DMERR_FWRITE, |
880 "PCX: Could not write colormap.\n"); | 883 "PCX: Could not write colormap.\n"); |
881 goto error; | 884 goto error; |
882 } | 885 } |
883 | 886 |
884 if (!dm_fwrite_byte(pcx.fp, hdr.reserved) || | 887 if (!dm_fwrite_byte(pcx.fp, hdr.reserved) || |
885 !dm_fwrite_byte(pcx.fp, hdr.nplanes) || | 888 !dm_fwrite_byte(pcx.fp, hdr.nplanes) || |
886 !dm_fwrite_le16(pcx.fp, hdr.bpl) || | 889 !dm_fwrite_le16(pcx.fp, hdr.bpl) || |
887 !dm_fwrite_le16(pcx.fp, hdr.palinfo) || | 890 !dm_fwrite_le16(pcx.fp, hdr.palInfo) || |
888 !dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler))) | 891 !dm_fwrite_str(pcx.fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler))) |
889 { | 892 { |
890 res = dmError(DMERR_FWRITE, | 893 res = dmError(DMERR_FWRITE, |
891 "PCX: Could not write header remainder.\n"); | 894 "PCX: Could not write header remainder.\n"); |
892 goto error; | 895 goto error; |
975 int dmReadPCXImageFILE(FILE *fp, DMImage **pimg) | 978 int dmReadPCXImageFILE(FILE *fp, DMImage **pimg) |
976 { | 979 { |
977 DMImage *img; | 980 DMImage *img; |
978 DMPCXData pcx; | 981 DMPCXData pcx; |
979 DMPCXHeader hdr; | 982 DMPCXHeader hdr; |
980 BOOL paletted; | 983 int res = 0; |
981 int res = 0, yc, xc; | |
982 Uint8 *dp; | |
983 | 984 |
984 pcx.buf = NULL; | 985 pcx.buf = NULL; |
985 | 986 |
986 // Read PCX header | 987 // Read PCX header |
987 if (!dm_fread_byte(fp, &hdr.manufacturer) || | 988 if (!dm_fread_byte(fp, &hdr.manufacturer) || |
988 !dm_fread_byte(fp, &hdr.version) || | 989 !dm_fread_byte(fp, &hdr.version) || |
989 !dm_fread_byte(fp, &hdr.encoding) || | 990 !dm_fread_byte(fp, &hdr.encoding) || |
990 !dm_fread_byte(fp, &hdr.bpp)) | 991 !dm_fread_byte(fp, &hdr.bitsPerPlane)) |
991 { | 992 { |
992 res = dmError(DMERR_FREAD, | 993 res = dmError(DMERR_FREAD, |
993 "PCX: Could not read basic header data.\n"); | 994 "PCX: Could not read basic header data.\n"); |
994 goto error; | 995 goto error; |
995 } | 996 } |
996 | 997 |
997 if (hdr.manufacturer != 10 || | 998 if (hdr.manufacturer != 10 || |
998 hdr.version != 5 || | 999 hdr.version != 5 || |
999 hdr.encoding != 1 || | 1000 hdr.encoding != 1) |
1000 hdr.bpp != 8) | |
1001 { | 1001 { |
1002 res = dmError(DMERR_NOT_SUPPORTED, | 1002 res = dmError(DMERR_NOT_SUPPORTED, |
1003 "PCX: Not a PCX file, or unsupported variant.\n"); | 1003 "PCX: Not a PCX file, or unsupported variant.\n"); |
1004 goto error; | 1004 goto error; |
1005 } | 1005 } |
1014 res = dmError(DMERR_FREAD, | 1014 res = dmError(DMERR_FREAD, |
1015 "PCX: Could not read image dimensions.\n"); | 1015 "PCX: Could not read image dimensions.\n"); |
1016 goto error; | 1016 goto error; |
1017 } | 1017 } |
1018 | 1018 |
1019 if (!dm_fread_str(fp, (Uint8 *) &hdr.colormap, sizeof(hdr.colormap))) | 1019 if (!dm_fread_str(fp, (Uint8 *) &hdr.colorMap, sizeof(hdr.colorMap))) |
1020 { | 1020 { |
1021 res = dmError(DMERR_FREAD, | 1021 res = dmError(DMERR_FREAD, |
1022 "PCX: Could not read colormap.\n"); | 1022 "PCX: Could not read colormap.\n"); |
1023 goto error; | 1023 goto error; |
1024 } | 1024 } |
1025 | 1025 |
1026 if (!dm_fread_byte(fp, &hdr.reserved) || | 1026 if (!dm_fread_byte(fp, &hdr.reserved) || |
1027 !dm_fread_byte(fp, &hdr.nplanes) || | 1027 !dm_fread_byte(fp, &hdr.nplanes) || |
1028 !dm_fread_le16(fp, &hdr.bpl) || | 1028 !dm_fread_le16(fp, &hdr.bpl) || |
1029 !dm_fread_le16(fp, &hdr.palinfo) || | 1029 !dm_fread_le16(fp, &hdr.palInfo) || |
1030 !dm_fread_str(fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler))) | 1030 !dm_fread_str(fp, (Uint8 *) &hdr.filler, sizeof(hdr.filler))) |
1031 { | 1031 { |
1032 res = dmError(DMERR_FREAD, | 1032 res = dmError(DMERR_FREAD, |
1033 "PCX: Could not read header remainder.\n"); | 1033 "PCX: Could not read header remainder.\n"); |
1034 goto error; | 1034 goto error; |
1035 } | 1035 } |
1036 | 1036 |
1037 if (hdr.nplanes != 3 && hdr.nplanes != 1) | 1037 if (hdr.nplanes < 1 || hdr.nplanes > 8) |
1038 { | 1038 { |
1039 res = dmError(DMERR_FREAD, | 1039 res = dmError(DMERR_NOT_SUPPORTED, |
1040 "PCX: Unsupported number of bitplanes %d.\n", | 1040 "PCX: Unsupported number of bitplanes %d.\n", |
1041 hdr.nplanes); | 1041 hdr.nplanes); |
1042 goto error; | 1042 goto error; |
1043 } | 1043 } |
1044 | 1044 |
1048 res = dmError(DMERR_MALLOC, | 1048 res = dmError(DMERR_MALLOC, |
1049 "PCX: Could not allocate image structure.\n"); | 1049 "PCX: Could not allocate image structure.\n"); |
1050 goto error; | 1050 goto error; |
1051 } | 1051 } |
1052 | 1052 |
1053 paletted = hdr.nplanes == 1; | 1053 if (hdr.bpl < (img->width * hdr.bitsPerPlane) / 8) |
1054 { | |
1055 res = dmError(DMERR_MALLOC, | |
1056 "PCX: The bytes per plane line value %d is smaller than width*bpp/8 = %d!\n", | |
1057 hdr.bpl, (img->width * hdr.bitsPerPlane) / 8); | |
1058 goto error; | |
1059 } | |
1060 | |
1054 pcx.bufLen = hdr.nplanes * hdr.bpl; | 1061 pcx.bufLen = hdr.nplanes * hdr.bpl; |
1055 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) | 1062 if ((pcx.buf = dmMalloc(pcx.bufLen)) == NULL) |
1056 { | 1063 { |
1057 res = dmError(DMERR_MALLOC, | 1064 res = dmError(DMERR_MALLOC, |
1058 "PCX: Could not allocate RLE buffer.\n"); | 1065 "PCX: Could not allocate RLE buffer.\n"); |
1059 goto error; | 1066 goto error; |
1060 } | 1067 } |
1061 | 1068 |
1062 // Read image data | 1069 // Read image data |
1063 dp = img->data; | 1070 Uint8 *dp = img->data; |
1064 for (yc = 0; yc < img->height; yc++) | 1071 for (int yc = 0; yc < img->height; yc++) |
1065 { | 1072 { |
1066 // Decode row of RLE'd data | 1073 // Decode row of RLE'd data |
1067 if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen)) | 1074 if (!dmPCXDecodeRLERow(fp, pcx.buf, pcx.bufLen)) |
1068 { | 1075 { |
1069 res = dmError(DMERR_INVALID_DATA, | 1076 res = dmError(DMERR_INVALID_DATA, |
1070 "PCX: Error decoding RLE data.\n"); | 1077 "PCX: Error decoding RLE compressed data.\n"); |
1071 goto error; | 1078 goto error; |
1072 } | 1079 } |
1073 | 1080 |
1074 // Decode bitplanes | 1081 // Decode bitplanes |
1075 switch (hdr.nplanes) | 1082 switch (hdr.bitsPerPlane) |
1076 { | 1083 { |
1077 case 1: | 1084 case 8: |
1078 memcpy(dp, pcx.buf, img->width); | 1085 for (int nplane = 0; nplane < hdr.nplanes; nplane++) |
1079 break; | |
1080 | |
1081 case 3: | |
1082 { | 1086 { |
1083 Uint8 *dptr = dp, | 1087 Uint8 *dptr = dp + nplane, |
1084 *sptr1 = pcx.buf, | 1088 *sptr = pcx.buf + (hdr.bpl * nplane); |
1085 *sptr2 = sptr1 + hdr.bpl, | 1089 |
1086 *sptr3 = sptr2 + hdr.bpl; | 1090 for (int xc = 0; xc < img->width; xc += hdr.nplanes, dptr += hdr.nplanes, sptr++) |
1087 | 1091 *dptr = *sptr; |
1088 for (xc = 0; xc < img->width; xc++) | |
1089 { | |
1090 *dptr++ = *sptr1++; | |
1091 *dptr++ = *sptr2++; | |
1092 *dptr++ = *sptr3++; | |
1093 } | |
1094 } | 1092 } |
1095 break; | 1093 break; |
1094 | |
1095 default: | |
1096 res = dmError(DMERR_INVALID_DATA, | |
1097 "PCX: Unsupported number of bits per plane %d.\n", | |
1098 hdr.bitsPerPlane); | |
1099 goto error; | |
1096 } | 1100 } |
1097 | 1101 |
1098 dp += img->pitch; | 1102 dp += img->pitch; |
1099 } | 1103 } |
1100 | 1104 |
1101 // Read VGA palette | 1105 // Read additional VGA palette, if available |
1102 if (paletted) | 1106 { |
1103 { | 1107 int ncolors; |
1104 int i, ncolors; | |
1105 Uint8 tmpb; | 1108 Uint8 tmpb; |
1106 BOOL read; | 1109 BOOL read; |
1107 | 1110 |
1108 if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C) | 1111 if (!dm_fread_byte(fp, &tmpb) || tmpb != 0x0C) |
1109 { | 1112 { |
1123 goto error; | 1126 goto error; |
1124 } | 1127 } |
1125 | 1128 |
1126 if (read) | 1129 if (read) |
1127 { | 1130 { |
1131 // Okay, attempt to read the palette data | |
1128 if (!dmReadPaletteData(fp, img->pal, ncolors)) | 1132 if (!dmReadPaletteData(fp, img->pal, ncolors)) |
1129 { | 1133 { |
1130 res = dmError(DMERR_FREAD, | 1134 res = dmError(DMERR_FREAD, |
1131 "PCX: Error reading palette.\n"); | 1135 "PCX: Error reading palette.\n"); |
1132 goto error; | 1136 goto error; |
1133 } | 1137 } |
1134 } | 1138 } |
1135 else | 1139 else |
1136 { | 1140 { |
1137 for (i = 0; i < img->ncolors; i++) | 1141 // If the extra palette is not available, copy the colors from |
1138 { | 1142 // the header palette to our internal palette structure. |
1139 if (i < 16) | 1143 for (int i = 0; i < (img->ncolors > DMPCX_PAL_COLORS ? DMPCX_PAL_COLORS : img->ncolors); i++) |
1140 { | 1144 { |
1141 img->pal[i].r = hdr.colormap[i].r; | 1145 img->pal[i].r = hdr.colorMap[i].r; |
1142 img->pal[i].g = hdr.colormap[i].g; | 1146 img->pal[i].g = hdr.colorMap[i].g; |
1143 img->pal[i].b = hdr.colormap[i].b; | 1147 img->pal[i].b = hdr.colorMap[i].b; |
1144 } | |
1145 } | 1148 } |
1146 } | 1149 } |
1147 } | 1150 } |
1148 | 1151 |
1149 error: | 1152 error: |