Mercurial > hg > dmlib
comparison tools/libgfx.c @ 1896:f80b2dc77c30
Work begins on IFF ILBM/PBM image writer. It is pretty broken, some things
will not work and some things are hardcoded. The ByteRun1 compression
implementation is somewhat inefficient. Interleaved files do not work yet.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Tue, 26 Jun 2018 03:13:38 +0300 |
parents | eb03869a10d3 |
children | 50dbfc10f49f |
comparison
equal
deleted
inserted
replaced
1895:eb03869a10d3 | 1896:f80b2dc77c30 |
---|---|
1910 | 1910 |
1911 return res; | 1911 return res; |
1912 } | 1912 } |
1913 | 1913 |
1914 | 1914 |
1915 static int dmWriteIFFChunkHdr(DMResource *fp, DMIFFChunk *chunk, const Uint32 id) | |
1916 { | |
1917 chunk->offs = dmftell(fp); | |
1918 chunk->id = id; | |
1919 dmMakeIFFChunkIDStr(chunk); | |
1920 | |
1921 if (!dmf_write_be32(fp, chunk->id) || | |
1922 !dmf_write_be32(fp, chunk->size)) | |
1923 { | |
1924 return dmError(DMERR_FREAD, | |
1925 "IFF: Could not write IFF '%s' chunk header.\n", | |
1926 chunk->idStr); | |
1927 } | |
1928 else | |
1929 return DMERR_OK; | |
1930 } | |
1931 | |
1932 | |
1933 static int dmWriteIFFChunkFinish(DMResource *fp, DMIFFChunk *chunk) | |
1934 { | |
1935 off_t curr = dmftell(fp); | |
1936 if (curr < 0) | |
1937 return dmferror(fp); | |
1938 | |
1939 chunk->size = curr - chunk->offs - (sizeof(Uint32) * 2); | |
1940 if ((chunk->size & 1) && !dmf_write_byte(fp, 0)) | |
1941 return dmferror(fp); | |
1942 | |
1943 if (dmfseek(fp, chunk->offs, SEEK_SET) < 0) | |
1944 return dmferror(fp); | |
1945 | |
1946 if (!dmf_write_be32(fp, chunk->id) || | |
1947 !dmf_write_be32(fp, chunk->size)) | |
1948 { | |
1949 return dmError(DMERR_FREAD, | |
1950 "IFF: Could not write IFF '%s' chunk header.\n", | |
1951 chunk->idStr); | |
1952 } | |
1953 | |
1954 if (dmfseek(fp, curr, SEEK_SET) < 0) | |
1955 return dmferror(fp); | |
1956 | |
1957 return DMERR_OK; | |
1958 } | |
1959 | |
1960 | |
1961 enum | |
1962 { | |
1963 DMODE_LIT, | |
1964 DMODE_RLE, | |
1965 }; | |
1966 | |
1967 | |
1968 BOOL dmIFFEncodeByteRun1Flush( | |
1969 DMResource *fp, const int mode, const BOOL flush, | |
1970 size_t *l_offs, const size_t offs, const Uint8 *buf, | |
1971 const Uint8 data, unsigned int *r_count) | |
1972 { | |
1973 if (mode == DMODE_LIT) | |
1974 { | |
1975 if (offs - *l_offs > *r_count || flush) | |
1976 { | |
1977 size_t count = (offs - *l_offs) - *r_count; | |
1978 Sint8 tmp = count - 1; | |
1979 | |
1980 if (!dmf_write_byte(fp, tmp) || | |
1981 !dmf_write_str(fp, buf + *l_offs, count)) | |
1982 return FALSE; | |
1983 } | |
1984 (*r_count)++; | |
1985 } | |
1986 else | |
1987 { | |
1988 unsigned int count = *r_count; | |
1989 Sint8 tmp = -(count - 1); | |
1990 | |
1991 if (!dmf_write_byte(fp, tmp) || | |
1992 !dmf_write_byte(fp, data)) | |
1993 return FALSE; | |
1994 | |
1995 *r_count = 0; | |
1996 *l_offs = offs; | |
1997 } | |
1998 | |
1999 return TRUE; | |
2000 } | |
2001 | |
2002 | |
2003 BOOL dmIFFEncodeByteRun1Row(DMResource *fp, const Uint8 *buf, const size_t bufLen) | |
2004 { | |
2005 size_t l_offs = 0, offs = 0; | |
2006 unsigned int r_count = 0; | |
2007 int prev = -1, mode = DMODE_LIT; | |
2008 | |
2009 for (offs = 0; offs < bufLen; offs++) | |
2010 { | |
2011 Uint8 data = buf[offs]; | |
2012 int next_mode; | |
2013 | |
2014 if (data == prev) | |
2015 { | |
2016 r_count++; | |
2017 next_mode = DMODE_RLE; | |
2018 } | |
2019 else | |
2020 { | |
2021 next_mode = DMODE_LIT; | |
2022 } | |
2023 | |
2024 BOOL flush = offs - l_offs >= 126 || r_count >= 126; | |
2025 if ((next_mode != mode || flush) && | |
2026 !dmIFFEncodeByteRun1Flush(fp, mode, flush, &l_offs, offs, buf, prev, &r_count)) | |
2027 return FALSE; | |
2028 | |
2029 mode = next_mode; | |
2030 prev = data; | |
2031 } | |
2032 | |
2033 if (!dmIFFEncodeByteRun1Flush(fp, mode, TRUE, &l_offs, offs, buf, prev, &r_count)) | |
2034 return FALSE; | |
2035 | |
2036 return TRUE; | |
2037 } | |
2038 | |
2039 | |
2040 static BOOL dmIFFWriteOneRow(DMResource *fp, DMIFF *iff, const Uint8 *buf, const size_t bufLen) | |
2041 { | |
2042 if (iff->bmhd.compression == IFF_COMP_BYTERUN1) | |
2043 return dmIFFEncodeByteRun1Row(fp, buf, bufLen); | |
2044 else | |
2045 return dmf_write_str(fp, buf, bufLen); | |
2046 } | |
2047 | |
2048 | |
2049 static inline Uint8 dmEncodeBit(const Uint8 *buf, const int xc) | |
2050 { | |
2051 return (buf[xc] >> (7 - (xc & 7))) & 1; | |
2052 } | |
2053 | |
2054 | |
2055 void dmEncodeBitPlane(Uint8 *dp, const Uint8 *src, const int width, const int nplane) | |
2056 { | |
2057 for (int xc = 0; xc < width; xc++) | |
2058 dp[xc / 8] |= dmEncodeBit(src, xc) << nplane; | |
2059 } | |
2060 | |
2061 | |
2062 int dmEncodeILBMBody(DMResource *fp, DMIFF *iff, const DMImage *img) | |
2063 { | |
2064 Uint8 *buf; | |
2065 size_t bufLen; | |
2066 int res = DMERR_OK; | |
2067 const int nplanes = iff->bmhd.nplanes; | |
2068 | |
2069 // Allocate planar encoding buffer | |
2070 bufLen = ((img->width + 15) / 16) * 2; | |
2071 if ((buf = dmMalloc(bufLen)) == NULL) | |
2072 return DMERR_MALLOC; | |
2073 | |
2074 dmMsg(2, "IFF: plane row size %d bytes.\n", bufLen); | |
2075 | |
2076 // Encode the chunk | |
2077 for (int yc = 0; yc < img->height; yc++) | |
2078 { | |
2079 const Uint8 *sp = img->data + (yc * img->pitch); | |
2080 | |
2081 dmMemset(buf, 0, bufLen); | |
2082 | |
2083 for (int plane = 0; plane < nplanes; plane++) | |
2084 { | |
2085 // Encode bitplane | |
2086 dmEncodeBitPlane(buf, sp, img->width, plane); | |
2087 | |
2088 // Compress / write data | |
2089 if (!dmIFFWriteOneRow(fp, iff, buf, bufLen)) | |
2090 { | |
2091 res = dmError(DMERR_FWRITE, | |
2092 "IFF: Error in writing image plane #%d @ %d.\n", | |
2093 plane, yc); | |
2094 goto error; | |
2095 } | |
2096 } | |
2097 } | |
2098 | |
2099 error: | |
2100 dmFree(buf); | |
2101 return res; | |
2102 } | |
2103 | |
2104 | |
2105 int dmEncodePBMBody(DMResource *fp, DMIFF *iff, const DMImage *img) | |
2106 { | |
2107 for (int yc = 0; yc < img->height; yc++) | |
2108 { | |
2109 if (!dmIFFWriteOneRow(fp, iff, img->data + (yc * img->pitch), img->width)) | |
2110 { | |
2111 return dmError(DMERR_FWRITE, | |
2112 "IFF: Error writing PBM image row #%d.\n", yc); | |
2113 } | |
2114 } | |
2115 | |
2116 return DMERR_OK; | |
2117 } | |
2118 | |
2119 | |
2120 int dmWriteIFFImage(DMResource *fp, const DMImage *img, const DMImageConvSpec *spec) | |
2121 { | |
2122 Uint32 idsig; | |
2123 DMIFF iff; | |
2124 int res = DMERR_OK; | |
2125 | |
2126 // XXX: Non-paletted ILBM not supported! | |
2127 if (!spec->paletted) | |
2128 { | |
2129 return dmError(DMERR_NOT_SUPPORTED, | |
2130 "Non-paletted IFF is not supported.\n"); | |
2131 } | |
2132 | |
2133 // Setup headers | |
2134 iff.bmhd.x = 0; | |
2135 iff.bmhd.y = 0; | |
2136 iff.bmhd.w = img->width; | |
2137 iff.bmhd.h = img->height; | |
2138 iff.bmhd.pagew = img->width; | |
2139 iff.bmhd.pageh = img->height; | |
2140 iff.bmhd.pad1 = 0; | |
2141 iff.bmhd.xasp = 1; // XXX TODO: compute the xasp/yasp from the img->aspect | |
2142 iff.bmhd.yasp = 1; | |
2143 | |
2144 iff.camg = 0; // XXX TODO: when/if HAM support | |
2145 iff.bmhd.masking = (img->ctransp < 0) ? IFF_MASK_NONE : spec->mask; | |
2146 iff.bmhd.compression = spec->compression ? IFF_COMP_BYTERUN1 : IFF_COMP_NONE; | |
2147 iff.bmhd.transp = (img->ctransp >= 0 && spec->mask == IFF_MASK_TRANSP) ? img->ctransp : 0xffff; | |
2148 iff.bmhd.nplanes = spec->nplanes; | |
2149 idsig = spec->planar ? IFF_ID_ILBM : IFF_ID_PBM; | |
2150 | |
2151 dmMsg(2, "IFF: nplanes=%d, comp=%d, mask=%d\n", | |
2152 iff.bmhd.nplanes, iff.bmhd.compression, iff.bmhd.masking); | |
2153 | |
2154 // Write IFF FORM header | |
2155 if ((res = dmWriteIFFChunkHdr(fp, &iff.chFORM, IFF_ID_FORM)) != DMERR_OK) | |
2156 goto out; | |
2157 | |
2158 // Write IFF ILBM/PBM signature | |
2159 if (!dmf_write_be32(fp, idsig)) | |
2160 { | |
2161 res = dmError(DMERR_FWRITE, | |
2162 "IFF: Error writing %s signature.\n", | |
2163 spec->planar ? "ILBM" : "PBM"); | |
2164 goto out; | |
2165 } | |
2166 | |
2167 // Write BMHD chunk and data | |
2168 if ((res = dmWriteIFFChunkHdr(fp, &iff.chBMHD, IFF_ID_BMHD)) != DMERR_OK || | |
2169 !dmf_write_be16(fp, iff.bmhd.w) || | |
2170 !dmf_write_be16(fp, iff.bmhd.h) || | |
2171 !dmf_write_be16(fp, iff.bmhd.x) || | |
2172 !dmf_write_be16(fp, iff.bmhd.y) || | |
2173 !dmf_write_byte(fp, iff.bmhd.nplanes) || | |
2174 !dmf_write_byte(fp, iff.bmhd.masking) || | |
2175 !dmf_write_byte(fp, iff.bmhd.compression) || | |
2176 !dmf_write_byte(fp, iff.bmhd.pad1) || | |
2177 !dmf_write_be16(fp, iff.bmhd.transp) || | |
2178 !dmf_write_byte(fp, iff.bmhd.xasp) || | |
2179 !dmf_write_byte(fp, iff.bmhd.yasp) || | |
2180 !dmf_write_be16(fp, iff.bmhd.pagew) || | |
2181 !dmf_write_be16(fp, iff.bmhd.pageh)) | |
2182 { | |
2183 res = dmError(DMERR_FWRITE, | |
2184 "IFF: Error writing BMHD chunk.\n"); | |
2185 goto out; | |
2186 } | |
2187 | |
2188 if ((res = dmWriteIFFChunkFinish(fp, &iff.chBMHD)) != DMERR_OK) | |
2189 goto out; | |
2190 | |
2191 // | |
2192 // CMAP | |
2193 // | |
2194 if (img->ncolors > 0 && spec->paletted) | |
2195 { | |
2196 if ((res = dmWriteIFFChunkHdr(fp, &iff.chCMAP, IFF_ID_CMAP)) != DMERR_OK) | |
2197 goto out; | |
2198 | |
2199 for (int i = 0; i < img->ncolors; i++) | |
2200 { | |
2201 DMColor *col = &img->pal[i]; | |
2202 if (!dmf_write_byte(fp, col->r) || | |
2203 !dmf_write_byte(fp, col->g) || | |
2204 !dmf_write_byte(fp, col->b)) | |
2205 { | |
2206 res = dmError(DMERR_FWRITE, | |
2207 "IFF: Could not write CMAP palette entry #%d.\n", i); | |
2208 goto out; | |
2209 } | |
2210 } | |
2211 | |
2212 if ((res = dmWriteIFFChunkFinish(fp, &iff.chCMAP)) != DMERR_OK) | |
2213 goto out; | |
2214 | |
2215 dmMsg(2, "IFF: CMAP %d entries (%d bytes)\n", | |
2216 img->ncolors, iff.chCMAP.size); | |
2217 } | |
2218 | |
2219 // | |
2220 // CAMG | |
2221 // | |
2222 if ((res = dmWriteIFFChunkHdr(fp, &iff.chCMAP, IFF_ID_CAMG)) != DMERR_OK) | |
2223 goto out; | |
2224 | |
2225 if (!dmf_write_be32(fp, iff.camg)) | |
2226 { | |
2227 return dmError(DMERR_FREAD, | |
2228 "IFF: Error writing CAMG chunk.\n"); | |
2229 } | |
2230 | |
2231 if ((res = dmWriteIFFChunkFinish(fp, &iff.chCMAP)) != DMERR_OK) | |
2232 goto out; | |
2233 | |
2234 // | |
2235 // Encode the body | |
2236 // | |
2237 if ((res = dmWriteIFFChunkHdr(fp, &iff.chBODY, IFF_ID_BODY)) != DMERR_OK) | |
2238 goto out; | |
2239 | |
2240 if (spec->planar) | |
2241 { | |
2242 if ((res = dmEncodeILBMBody(fp, &iff, img)) != DMERR_OK) | |
2243 goto out; | |
2244 } | |
2245 else | |
2246 { | |
2247 if ((res = dmEncodePBMBody(fp, &iff, img)) != DMERR_OK) | |
2248 goto out; | |
2249 } | |
2250 | |
2251 if ((res = dmWriteIFFChunkFinish(fp, &iff.chBODY)) != DMERR_OK) | |
2252 goto out; | |
2253 | |
2254 // Finish the FORM chunk | |
2255 if ((res = dmWriteIFFChunkFinish(fp, &iff.chFORM)) != DMERR_OK) | |
2256 goto out; | |
2257 | |
2258 out: | |
2259 return res; | |
2260 } | |
2261 | |
2262 | |
1915 // | 2263 // |
1916 // List of formats | 2264 // List of formats |
1917 // | 2265 // |
1918 const DMImageFormat dmImageFormatList[] = | 2266 const DMImageFormat dmImageFormatList[] = |
1919 { | 2267 { |
1935 fmtProbePCX, dmReadPCXImage, dmWritePCXImage, | 2283 fmtProbePCX, dmReadPCXImage, dmWritePCXImage, |
1936 }, | 2284 }, |
1937 { | 2285 { |
1938 "iff", "IFF ILBM / PBM", | 2286 "iff", "IFF ILBM / PBM", |
1939 DM_IMGFMT_IFF, DM_FMT_RDWR, | 2287 DM_IMGFMT_IFF, DM_FMT_RDWR, |
1940 fmtProbeIFF, dmReadIFFImage, | 2288 fmtProbeIFF, dmReadIFFImage, dmWriteIFFImage, |
1941 NULL, | |
1942 }, | 2289 }, |
1943 { | 2290 { |
1944 "raw", "Plain bitplaned (planar or non-planar) RAW", | 2291 "raw", "Plain bitplaned (planar or non-planar) RAW", |
1945 DM_IMGFMT_RAW, DM_FMT_WR, | 2292 DM_IMGFMT_RAW, DM_FMT_WR, |
1946 NULL, NULL, dmWriteRAWImage, | 2293 NULL, NULL, dmWriteRAWImage, |