44 #if PNG_LIBPNG_VER < 10400
46 #define png_set_expand_gray_1_2_4_to_8 png_set_gray_1_2_4_to_8
58 #define FILE_BUFFER_CAPACITY (1024*4)
60 #define ROUNDCLAMPF(x) ((x < 0.0f) ? 0 : \
61 ((x > 1.0f) ? 255 : (uint8_t)(255.0f*(x) + 0.5f)))
62 #define ROUNDCLAMP(x) ((x < 0.0) ? 0 : \
63 ((x > 1.0) ? 255 : (uint8_t)(255.0*(x) + 0.5)))
67 static int StringEndsWith(
const char *String,
const char *Suffix)
69 unsigned i, StringLength = strlen(String), SuffixLength = strlen(Suffix);
71 if(StringLength < SuffixLength)
74 String += StringLength - SuffixLength;
76 for(i = 0; i < SuffixLength; i++)
77 if(tolower(String[i]) != tolower(Suffix[i]))
85 static void FillImage(uint32_t *Image,
int Width,
int Height, uint32_t Color)
90 for(y = 0; y < Height; y++, Image += Width)
91 for(x = 0; x < Width; x++)
110 static uint32_t *GetImagePalette(
int *NumColors,
int *UseColor,
int *UseAlpha,
111 const uint32_t *Image,
int Width,
int Height)
113 const int MaxColors = 256;
114 uint32_t *Palette = NULL;
116 int x, y, i, Red, Green, Blue, Alpha;
119 if(!UseColor || !NumColors || !UseAlpha)
122 || !(Palette = (uint32_t *)
Malloc(
sizeof(uint32_t)*MaxColors)))
125 *UseColor = *UseAlpha = 1;
129 *NumColors = *UseColor = *UseAlpha = 0;
131 for(y = 0; y < Height; y++)
133 for(x = 0; x < Width; x++)
136 Red = ((uint8_t *)&Pixel)[0];
137 Green = ((uint8_t *)&Pixel)[1];
138 Blue = ((uint8_t *)&Pixel)[2];
139 Alpha = ((uint8_t *)&Pixel)[3];
141 if(Red != Green || Red != Blue)
148 for(i = 0; i < *NumColors; i++)
149 if(Pixel == Palette[i])
174 static uint16_t ReadWordLE(FILE *File)
177 w = (uint16_t) getc(File);
178 w |= ((uint16_t) getc(File) << 8);
184 static uint32_t ReadDWordLE(FILE *File)
187 dw = (uint32_t) getc(File);
188 dw |= ((uint32_t) getc(File) << 8);
189 dw |= ((uint32_t) getc(File) << 16);
190 dw |= ((uint32_t) getc(File) << 24);
196 static void WriteWordLE(uint16_t w, FILE *File)
198 putc(w & 0xFF, File);
199 putc((w & 0xFF00) >> 8, File);
204 static void WriteDWordLE(uint32_t dw, FILE *File)
206 putc(dw & 0xFF, File);
207 putc((dw & 0xFF00) >> 8, File);
208 putc((dw & 0xFF0000) >> 16, File);
209 putc((dw & 0xFF000000) >> 24, File);
214 static int ReadBmp1Bit(uint32_t *Image,
int Width,
int Height, FILE *File,
const uint32_t *Palette)
216 int RowPadding = (-(Width+7)/8)&3;
220 Image += ((
long int)Width)*((
long int)Height - 1);
222 for(y = Height; y; y--, Image -= Width)
227 for(x = 0; x < Width;)
231 for(Bit = 7; Bit >= 0 && x < Width; Bit--, Code <<= 1)
232 Image[x++] = Palette[(Code & 0x80) ? 1:0];
235 for(x = RowPadding; x; x--)
244 static int ReadBmp4Bit(uint32_t *Image,
int Width,
int Height, FILE *File,
const uint32_t *Palette)
246 int RowPadding = (-(Width+1)/2)&3;
250 Image += ((
long int)Width)*((
long int)Height - 1);
252 for(y = Height; y; y--, Image -= Width)
257 for(x = 0; x < Width;)
260 Image[x++] = Palette[(Code & 0xF0) >> 4];
263 Image[x++] = Palette[Code & 0x0F];
266 for(x = RowPadding; x; x--)
275 static int ReadBmp4BitRle(uint32_t *Image,
int Width,
int Height, FILE *File,
const uint32_t *Palette)
278 unsigned Count, Value;
279 uint32_t ColorH, ColorL;
281 FillImage(Image, Width, Height, Palette[0]);
282 Image += ((
long int)Width)*((
long int)Height - 1);
284 for(x = 0, y = Height; y;)
309 if(x >= Width || y < 0)
321 Image[x++] = Palette[(Value & 0xF0) >> 4];
328 Image[x++] = Palette[Value & 0x0F];
336 if(((Count + 1)/2) & 1)
342 ColorH = Palette[(Value & 0xF0) >> 4];
343 ColorL = Palette[Value & 0xF];
373 static int ReadBmp8Bit(uint32_t *Image,
int Width,
int Height, FILE *File,
const uint32_t *Palette)
375 int RowPadding = (-Width)&3;
378 Image += ((
long int)Width)*((
long int)Height - 1);
380 for(y = Height; y; y--, Image -= Width)
385 for(x = 0; x < Width; x++)
386 Image[x] = Palette[getc(File) & 0xFF];
388 for(x = RowPadding; x; x--)
397 static int ReadBmp8BitRle(uint32_t *Image,
int Width,
int Height, FILE *File,
const uint32_t *Palette)
400 unsigned Count, Value;
403 FillImage(Image, Width, Height, Palette[0]);
404 Image += ((
long int)Width)*((
long int)Height - 1);
406 for(x = 0, y = Height; y;)
431 if(x >= Width || y < 0)
442 Image[x++] = Palette[getc(File) & 0xFF];
451 Color = Palette[Value & 0xFF];
468 static int ReadBmp24Bit(uint32_t *Image,
int Width,
int Height, FILE *File)
470 uint8_t *ImagePtr = (uint8_t *)Image;
471 int RowPadding = (-3*Width)&3;
476 ImagePtr += ((
long int)Width)*((
long int)Height - 1);
478 for(y = Height; y; y--, ImagePtr -= Width)
483 for(x = 0; x < Width; x += 4)
486 ImagePtr[x+2] = getc(File);
487 ImagePtr[x+1] = getc(File);
488 ImagePtr[x+0] = getc(File);
491 for(x = RowPadding; x; x--)
499 static void GetMaskShifts(uint32_t Mask,
int *LeftShift,
int *RightShift)
501 int Shift = 0, BitCount = 0;
524 Shift += BitCount - 8;
539 static int ReadBmp16Bit(uint32_t *Image,
int Width,
int Height, FILE *File,
540 uint32_t RedMask, uint32_t GreenMask, uint32_t BlueMask, uint32_t AlphaMask)
542 uint8_t *ImagePtr = (uint8_t *)Image;
544 int RowPadding = (-2*Width)&3;
545 int RedLeftShift, GreenLeftShift, BlueLeftShift, AlphaLeftShift;
546 int RedRightShift, GreenRightShift, BlueRightShift, AlphaRightShift;
549 GetMaskShifts(RedMask, &RedLeftShift, &RedRightShift);
550 GetMaskShifts(GreenMask, &GreenLeftShift, &GreenRightShift);
551 GetMaskShifts(BlueMask, &BlueLeftShift, &BlueRightShift);
552 GetMaskShifts(AlphaMask, &AlphaLeftShift, &AlphaRightShift);
554 ImagePtr += ((
long int)Width)*((
long int)Height - 1);
556 for(y = Height; y; y--, ImagePtr -= Width)
561 for(x = 0; x < Width; x += 4)
563 Code = ReadWordLE(File);
567 ImagePtr[x+3] = ((Code & AlphaMask) >> AlphaRightShift) << AlphaLeftShift;
568 ImagePtr[x+2] = ((Code & BlueMask ) >> BlueRightShift ) << BlueLeftShift;
569 ImagePtr[x+1] = ((Code & GreenMask) >> GreenRightShift) << GreenLeftShift;
570 ImagePtr[x+0] = ((Code & RedMask ) >> RedRightShift ) << RedLeftShift;
573 for(x = RowPadding; x; x--)
582 static int ReadBmp32Bit(uint32_t *Image,
int Width,
int Height, FILE *File,
583 uint32_t RedMask, uint32_t GreenMask, uint32_t BlueMask, uint32_t AlphaMask)
587 int RedLeftShift, GreenLeftShift, BlueLeftShift, AlphaLeftShift;
588 int RedRightShift, GreenRightShift, BlueRightShift, AlphaRightShift;
591 GetMaskShifts(RedMask, &RedLeftShift, &RedRightShift);
592 GetMaskShifts(GreenMask, &GreenLeftShift, &GreenRightShift);
593 GetMaskShifts(BlueMask, &BlueLeftShift, &BlueRightShift);
594 GetMaskShifts(AlphaMask, &AlphaLeftShift, &AlphaRightShift);
596 ImagePtr = (uint8_t *)Image + ((
long int)Width)*((
long int)Height - 1);
598 for(y = Height; y; y--, ImagePtr -= Width)
603 for(x = 0; x < Width; x += 4)
605 Code = ReadDWordLE(File);
609 ImagePtr[x+3] = ((Code & AlphaMask) >> AlphaRightShift) << AlphaLeftShift;
610 ImagePtr[x+2] = ((Code & BlueMask ) >> BlueRightShift ) << BlueLeftShift;
611 ImagePtr[x+1] = ((Code & GreenMask) >> GreenRightShift) << GreenLeftShift;
612 ImagePtr[x+0] = ((Code & RedMask ) >> RedRightShift ) << RedLeftShift;
632 static int ReadBmp(uint32_t **Image,
int *Width,
int *Height, FILE *File)
634 uint32_t *Palette = NULL;
636 long int ImageDataOffset, InfoSize;
637 unsigned i, NumPlanes, BitsPerPixel, Compression, NumColors;
638 uint32_t RedMask, GreenMask, BlueMask, AlphaMask;
639 int Success = 0, Os2Bmp;
643 *Width = *Height = 0;
644 fseek(File, 0, SEEK_SET);
646 Magic[0] = getc(File);
647 Magic[1] = getc(File);
649 if(!(Magic[0] == 0x42 && Magic[1] == 0x4D)
650 || fseek(File, 8, SEEK_CUR))
656 ImageDataOffset = ReadDWordLE(File);
657 InfoSize = ReadDWordLE(File);
666 if((Os2Bmp = (InfoSize == 12)))
668 *Width = (int)ReadWordLE(File);
669 *Height = (int)ReadWordLE(File);
670 NumPlanes = (unsigned)ReadWordLE(File);
671 BitsPerPixel = (unsigned)ReadWordLE(File);
674 RedMask = 0x00FF0000;
675 GreenMask = 0x0000FF00;
676 BlueMask = 0x000000FF;
677 AlphaMask = 0xFF000000;
681 *Width = abs((
int)ReadDWordLE(File));
682 *Height = abs((
int)ReadDWordLE(File));
683 NumPlanes = (unsigned)ReadWordLE(File);
684 BitsPerPixel = (unsigned)ReadWordLE(File);
685 Compression = (unsigned)ReadDWordLE(File);
686 fseek(File, 12, SEEK_CUR);
687 NumColors = (unsigned)ReadDWordLE(File);
688 fseek(File, 4, SEEK_CUR);
689 RedMask = ReadDWordLE(File);
690 GreenMask = ReadDWordLE(File);
691 BlueMask = ReadDWordLE(File);
692 AlphaMask = ReadDWordLE(File);
698 ErrorMessage(
"Image dimensions exceed MAX_IMAGE_SIZE.\n");
702 if(feof(File) || NumPlanes != 1 || Compression > 3)
706 if(!(*Image = (uint32_t *)
Malloc(
sizeof(uint32_t)*((
long int)*Width)*((
long int)*Height))))
710 if(BitsPerPixel <= 8)
712 fseek(File, 14 + InfoSize, SEEK_SET);
715 NumColors = 1 << BitsPerPixel;
717 if(!(Palette = (uint32_t *)
Malloc(
sizeof(uint32_t)*256)))
720 for(i = 0, PalettePtr = (uint8_t *)Palette; i < NumColors; i++)
723 PalettePtr[2] = getc(File);
724 PalettePtr[1] = getc(File);
725 PalettePtr[0] = getc(File);
733 Palette[i] = Palette[0];
736 if(fseek(File, ImageDataOffset, SEEK_SET) || feof(File))
749 Success = ReadBmp1Bit(*Image, *Width, *Height, File, Palette);
752 Success = ReadBmp4Bit(*Image, *Width, *Height, File, Palette);
755 Success = ReadBmp8Bit(*Image, *Width, *Height, File, Palette);
758 Success = ReadBmp24Bit(*Image, *Width, *Height, File);
761 Success = ReadBmp16Bit(*Image, *Width, *Height, File,
762 0x001F << 10, 0x001F << 5, 0x0001F, 0);
765 Success = ReadBmp32Bit(*Image, *Width, *Height, File,
766 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000);
771 if(BitsPerPixel == 8)
772 Success = ReadBmp8BitRle(*Image, *Width, *Height, File, Palette);
775 if(BitsPerPixel == 4)
776 Success = ReadBmp4BitRle(*Image, *Width, *Height, File, Palette);
782 Success = ReadBmp16Bit(*Image, *Width, *Height, File,
783 RedMask, GreenMask, BlueMask, AlphaMask);
786 Success = ReadBmp32Bit(*Image, *Width, *Height, File,
787 RedMask, GreenMask, BlueMask, AlphaMask);
800 if(!Success && *Image)
828 static int WriteBmp(
const uint32_t *Image,
int Width,
int Height, FILE *File)
830 const uint8_t *ImagePtr = (uint8_t *)Image;
831 uint32_t *Palette = NULL;
834 int UsePalette, NumColors, UseColor, UseAlpha;
835 int x, y, i, RowPadding, Success = 0;
841 Palette = GetImagePalette(&NumColors, &UseColor, &UseAlpha,
842 Image, Width, Height);
845 if(Palette && 2*NumColors < Width*Height)
848 UsePalette = NumColors = 0;
855 RowPadding = (-Width)&3;
856 ImageSize = (Width + RowPadding)*((
long int)Height);
860 RowPadding = (-3*Width)&3;
861 ImageSize = (3*Width + RowPadding)*((
long int)Height);
871 WriteDWordLE(54 + 4*NumColors + ImageSize, File);
873 WriteDWordLE(0, File);
874 WriteDWordLE(54 + 4*NumColors, File);
877 WriteDWordLE(40, File);
878 WriteDWordLE(Width, File);
879 WriteDWordLE(Height, File);
880 WriteWordLE(1, File);
881 WriteWordLE((UsePalette) ? 8:24, File);
882 WriteDWordLE(0, File);
883 WriteDWordLE(ImageSize, File);
884 WriteDWordLE(2835, File);
885 WriteDWordLE(2835, File);
888 WriteDWordLE((!UsePalette || NumColors == 256) ? 0:NumColors, File);
890 WriteDWordLE(0, File);
900 for(i = 0; i < NumColors; i++)
903 putc(((uint8_t *)&Pixel)[2], File);
904 putc(((uint8_t *)&Pixel)[1], File);
905 putc(((uint8_t *)&Pixel)[0], File);
912 ImagePtr += ((
long int)Width)*((
long int)Height - 1);
914 for(y = Height; y; y--, ImagePtr -= Width)
918 for(x = 0; x < Width; x += 4)
920 Pixel = *((uint32_t *)(ImagePtr + x));
922 for(i = 0; i < NumColors; i++)
923 if(Pixel == Palette[i])
931 for(x = 0; x < Width; x += 4)
933 putc(ImagePtr[x+2], File);
934 putc(ImagePtr[x+1], File);
935 putc(ImagePtr[x+0], File);
939 for(x = RowPadding; x; x--)
965 struct jpeg_error_mgr pub;
971 METHODDEF(
void) JerrExit(j_common_ptr cinfo)
973 hooked_jerr *Jerr = (hooked_jerr *) cinfo->err;
974 (*cinfo->err->output_message)(cinfo);
975 longjmp(Jerr->jmpbuf, 1);
992 static int ReadJpeg(uint32_t **Image,
int *Width,
int *Height, FILE *File)
994 struct jpeg_decompress_struct cinfo;
1001 *Width = *Height = 0;
1002 cinfo.err = jpeg_std_error(&Jerr.pub);
1003 Jerr.pub.error_exit = JerrExit;
1005 if(setjmp(Jerr.jmpbuf))
1008 jpeg_create_decompress(&cinfo);
1009 jpeg_stdio_src(&cinfo, File);
1010 jpeg_read_header(&cinfo, 1);
1011 cinfo.out_color_space = JCS_RGB;
1012 jpeg_start_decompress(&cinfo);
1013 *Width = (int)cinfo.output_width;
1014 *Height = (
int)cinfo.output_height;
1018 ErrorMessage(
"Image dimensions exceed MAX_IMAGE_SIZE.\n");
1019 jpeg_abort_decompress(&cinfo);
1024 if(!(*Image = (uint32_t *)
Malloc(
sizeof(uint32_t)
1025 *((
size_t)*Width)*((
size_t)*Height))))
1027 jpeg_abort_decompress(&cinfo);
1032 RowSize = cinfo.output_width * cinfo.output_components;
1033 Buffer = (*cinfo.mem->alloc_sarray) ((j_common_ptr) &cinfo,
1034 JPOOL_IMAGE, RowSize, 1);
1035 ImagePtr = (uint8_t *)*Image;
1037 while(cinfo.output_scanline < cinfo.output_height)
1038 for(jpeg_read_scanlines(&cinfo, Buffer, 1), i = 0; i < RowSize; i += 3)
1040 *(ImagePtr++) = Buffer[0][i];
1041 *(ImagePtr++) = Buffer[0][i+1];
1042 *(ImagePtr++) = Buffer[0][i+2];
1043 *(ImagePtr++) = 0xFF;
1046 jpeg_finish_decompress(&cinfo);
1047 jpeg_destroy_decompress(&cinfo);
1054 *Width = *Height = 0;
1055 jpeg_destroy_decompress(&cinfo);
1078 static int WriteJpeg(
const uint32_t *Image,
int Width,
int Height,
1079 FILE *File,
int Quality)
1081 struct jpeg_compress_struct cinfo;
1083 uint8_t *Buffer = 0, *ImagePtr;
1084 unsigned i, RowSize;
1090 cinfo.err = jpeg_std_error(&Jerr.pub);
1091 Jerr.pub.error_exit = JerrExit;
1093 if(setjmp(Jerr.jmpbuf))
1096 jpeg_create_compress(&cinfo);
1097 jpeg_stdio_dest(&cinfo, File);
1098 cinfo.image_width = Width;
1099 cinfo.image_height = Height;
1100 cinfo.input_components = 3;
1101 cinfo.in_color_space = JCS_RGB;
1102 jpeg_set_defaults(&cinfo);
1103 jpeg_set_quality(&cinfo, (Quality < 100) ? Quality : 100, 1);
1104 jpeg_start_compress(&cinfo, 1);
1107 ImagePtr = (uint8_t *)Image;
1109 if(!(Buffer = (uint8_t *)
Malloc(RowSize)))
1112 while(cinfo.next_scanline < cinfo.image_height)
1114 for(i = 0; i < RowSize; i += 3)
1116 Buffer[i] = ImagePtr[0];
1117 Buffer[i+1] = ImagePtr[1];
1118 Buffer[i+2] = ImagePtr[2];
1122 jpeg_write_scanlines(&cinfo, &Buffer, 1);
1128 jpeg_finish_compress(&cinfo);
1129 jpeg_destroy_compress(&cinfo);
1135 jpeg_destroy_compress(&cinfo);
1155 static int ReadPng(uint32_t **Image,
int *Width,
int *Height, FILE *File)
1157 png_bytep *RowPointers;
1161 png_uint_32 PngWidth, PngHeight;
1162 int BitDepth, ColorType, InterlaceType;
1166 *Width = *Height = 0;
1169 if(fread(Header, 1, 8, File) != 8 || png_sig_cmp(Header, 0, 8))
1173 if(!(Png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL))
1174 || !(Info = png_create_info_struct(Png)))
1177 png_destroy_read_struct(&Png, (png_infopp)NULL, (png_infopp)NULL);
1182 if(setjmp(png_jmpbuf(Png)))
1185 png_init_io(Png, File);
1186 png_set_sig_bytes(Png, 8);
1188 png_read_info(Png, Info);
1189 png_get_IHDR(Png, Info, &PngWidth, &PngHeight, &BitDepth, &ColorType,
1190 &InterlaceType, (
int*)NULL, (
int*)NULL);
1191 *Width = (int)PngWidth;
1192 *Height = (int)PngHeight;
1195 if(ColorType == PNG_COLOR_TYPE_PALETTE)
1196 png_set_palette_to_rgb(Png);
1197 if(ColorType == PNG_COLOR_TYPE_GRAY && BitDepth < 8)
1198 png_set_expand_gray_1_2_4_to_8(Png);
1199 if(ColorType == PNG_COLOR_TYPE_GRAY || ColorType == PNG_COLOR_TYPE_GRAY_ALPHA)
1200 png_set_gray_to_rgb(Png);
1201 if(png_get_valid(Png, Info, PNG_INFO_tRNS))
1202 png_set_tRNS_to_alpha(Png);
1204 png_set_strip_16(Png);
1205 png_set_filler(Png, 0xFF, PNG_FILLER_AFTER);
1207 png_set_interlace_handling(Png);
1208 png_read_update_info(Png, Info);
1211 if(!(*Image = (uint32_t *)
Malloc(
sizeof(uint32_t)
1212 *((
size_t)*Width)*((
size_t)*Height)))
1213 || !(RowPointers = (png_bytep *)
Malloc(
sizeof(png_bytep)
1217 for(Row = 0; Row < PngHeight; Row++)
1218 RowPointers[Row] = (png_bytep)(*Image + PngWidth*Row);
1221 png_read_image(Png, RowPointers);
1223 png_destroy_read_struct(&Png, &Info, (png_infopp)NULL);
1230 *Width = *Height = 0;
1231 png_destroy_read_struct(&Png, &Info, (png_infopp)NULL);
1256 static int WritePng(
const uint32_t *Image,
int Width,
int Height, FILE *File)
1258 const uint32_t *ImagePtr;
1259 uint32_t *Palette = NULL;
1263 png_color PngPalette[256];
1264 png_byte PngTrans[256];
1266 int PngColorType, NumColors, UseColor, UseAlpha;
1267 int x, y, i, Success = 0;
1273 if(!(RowBuffer = (uint8_t *)
Malloc(4*Width)))
1276 if(!(Png = png_create_write_struct(PNG_LIBPNG_VER_STRING,
1278 || !(Info = png_create_info_struct(Png)))
1281 png_destroy_write_struct(&Png, (png_infopp)NULL);
1287 if(setjmp(png_jmpbuf(Png)))
1293 png_init_io(Png, File);
1294 png_set_compression_level(Png, Z_BEST_COMPRESSION);
1296 Palette = GetImagePalette(&NumColors, &UseColor, &UseAlpha,
1297 Image, Width, Height);
1300 if(Palette && UseColor)
1301 PngColorType = PNG_COLOR_TYPE_PALETTE;
1303 PngColorType = PNG_COLOR_TYPE_RGB_ALPHA;
1305 PngColorType = PNG_COLOR_TYPE_RGB;
1307 PngColorType = PNG_COLOR_TYPE_GRAY;
1309 png_set_IHDR(Png, Info, Width, Height, 8, PngColorType,
1310 PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
1312 if(PngColorType == PNG_COLOR_TYPE_PALETTE)
1314 for(i = 0; i < NumColors; i++)
1317 PngPalette[i].red = ((uint8_t *)&Pixel)[0];
1318 PngPalette[i].green = ((uint8_t *)&Pixel)[1];
1319 PngPalette[i].blue = ((uint8_t *)&Pixel)[2];
1320 PngTrans[i] = ((uint8_t *)&Pixel)[3];
1323 png_set_PLTE(Png, Info, PngPalette, NumColors);
1326 png_set_tRNS(Png, Info, PngTrans, NumColors, NULL);
1329 png_write_info(Png, Info);
1331 for(y = 0, ImagePtr = Image; y < Height; y++, ImagePtr += Width)
1333 switch(PngColorType)
1335 case PNG_COLOR_TYPE_RGB_ALPHA:
1336 png_write_row(Png, (png_bytep)ImagePtr);
1338 case PNG_COLOR_TYPE_RGB:
1339 for(x = 0; x < Width; x++)
1341 Pixel = ImagePtr[x];
1342 RowBuffer[3*x + 0] = ((uint8_t *)&Pixel)[0];
1343 RowBuffer[3*x + 1] = ((uint8_t *)&Pixel)[1];
1344 RowBuffer[3*x + 2] = ((uint8_t *)&Pixel)[2];
1347 png_write_row(Png, (png_bytep)RowBuffer);
1349 case PNG_COLOR_TYPE_GRAY:
1350 for(x = 0; x < Width; x++)
1352 Pixel = ImagePtr[x];
1353 RowBuffer[x] = ((uint8_t *)&Pixel)[0];
1356 png_write_row(Png, (png_bytep)RowBuffer);
1358 case PNG_COLOR_TYPE_PALETTE:
1359 for(x = 0; x < Width; x++)
1361 Pixel = ImagePtr[x];
1363 for(i = 0; i < NumColors; i++)
1364 if(Pixel == Palette[i])
1370 png_write_row(Png, (png_bytep)RowBuffer);
1375 png_write_end(Png, Info);
1380 png_destroy_write_struct(&Png, &Info);
1401 static int ReadTiff(uint32_t **Image,
int *Width,
int *Height,
1402 const char *FileName,
unsigned Directory)
1405 uint32 ImageWidth, ImageHeight;
1408 *Width = *Height = 0;
1410 if(!(Tiff = TIFFOpen(FileName,
"r")))
1416 TIFFSetDirectory(Tiff, Directory);
1417 TIFFGetField(Tiff, TIFFTAG_IMAGEWIDTH, &ImageWidth);
1418 TIFFGetField(Tiff, TIFFTAG_IMAGELENGTH, &ImageHeight);
1419 *Width = (int)ImageWidth;
1420 *Height = (int)ImageHeight;
1424 ErrorMessage(
"Image dimensions exceed MAX_IMAGE_SIZE.\n");
1428 if(!(*Image = (uint32_t *)
Malloc(
sizeof(uint32_t)*ImageWidth*ImageHeight)))
1431 if(!TIFFReadRGBAImageOriented(Tiff, ImageWidth, ImageHeight,
1432 (uint32 *)*Image, ORIENTATION_TOPLEFT, 1))
1442 *Width = *Height = 0;
1461 static int WriteTiff(
const uint32_t *Image,
int Width,
int Height,
1462 const char *FileName)
1465 uint16 Alpha = EXTRASAMPLE_ASSOCALPHA;
1470 if(!(Tiff = TIFFOpen(FileName,
"w")))
1476 if(TIFFSetField(Tiff, TIFFTAG_IMAGEWIDTH, Width) != 1
1477 || TIFFSetField(Tiff, TIFFTAG_IMAGELENGTH, Height) != 1
1478 || TIFFSetField(Tiff, TIFFTAG_SAMPLESPERPIXEL, 4) != 1
1479 || TIFFSetField(Tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB) != 1
1480 || TIFFSetField(Tiff, TIFFTAG_EXTRASAMPLES, 1, &Alpha) != 1
1481 || TIFFSetField(Tiff, TIFFTAG_BITSPERSAMPLE, 8) != 1
1482 || TIFFSetField(Tiff, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT) != 1
1483 || TIFFSetField(Tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG) != 1
1486 || TIFFSetField(Tiff, TIFFTAG_COMPRESSION, COMPRESSION_LZW) != 1)
1493 if(TIFFWriteEncodedStrip(Tiff, 0, (tdata_t)Image,
1494 4*((
size_t)Width)*((
size_t)Height)) < 0)
1508 static void *ConvertToFormat(uint32_t *Src,
int Width,
int Height,
1511 const int NumPixels = Width*Height;
1512 const int NumChannels = (Format & IMAGEIO_GRAYSCALE) ?
1513 1 : ((Format & IMAGEIO_STRIP_ALPHA) ? 3 : 4);
1514 const int ChannelStride = (Format & IMAGEIO_PLANAR) ? NumPixels : 1;
1515 const int ChannelStride2 = 2*ChannelStride;
1516 const int ChannelStride3 = 3*ChannelStride;
1521 int Order[4] = {0, 1, 2, 3};
1522 int i, x, y, PixelStride, RowStride;
1525 PixelStride = (Format & IMAGEIO_PLANAR) ? 1 : NumChannels;
1527 if(Format & IMAGEIO_COLUMNMAJOR)
1529 RowStride = PixelStride;
1530 PixelStride *= Height;
1533 RowStride = Width*PixelStride;
1535 if(Format & IMAGEIO_BGRFLIP)
1541 if((Format & IMAGEIO_AFLIP) && !(Format & IMAGEIO_STRIP_ALPHA))
1543 Order[3] = Order[2];
1544 Order[2] = Order[1];
1545 Order[1] = Order[0];
1549 switch(Format & (IMAGEIO_U8 | IMAGEIO_SINGLE | IMAGEIO_DOUBLE))
1552 if(!(DestU8 = (uint8_t *)
Malloc(
sizeof(uint8_t)*NumChannels*NumPixels)))
1558 for(y = 0; y < Height; y++, Src += Width)
1559 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1562 DestU8[i] = (uint8_t)(0.299f*((uint8_t *)&Pixel)[0]
1563 + 0.587f*((uint8_t *)&Pixel)[1]
1564 + 0.114f*((uint8_t *)&Pixel)[2] + 0.5f);
1568 for(y = 0; y < Height; y++, Src += Width)
1569 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1572 DestU8[i] = ((uint8_t *)&Pixel)[Order[0]];
1573 DestU8[i + ChannelStride] = ((uint8_t *)&Pixel)[Order[1]];
1574 DestU8[i + ChannelStride2] = ((uint8_t *)&Pixel)[Order[2]];
1578 for(y = 0; y < Height; y++, Src += Width)
1579 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1582 DestU8[i] = ((uint8_t *)&Pixel)[Order[0]];
1583 DestU8[i + ChannelStride] = ((uint8_t *)&Pixel)[Order[1]];
1584 DestU8[i + ChannelStride2] = ((uint8_t *)&Pixel)[Order[2]];
1585 DestU8[i + ChannelStride3] = ((uint8_t *)&Pixel)[Order[3]];
1590 case IMAGEIO_SINGLE:
1591 if(!(DestF = (
float *)
Malloc(
sizeof(
float)*NumChannels*NumPixels)))
1597 for(y = 0; y < Height; y++, Src += Width)
1598 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1601 DestF[i] = 1.172549019607843070675535e-3f*((uint8_t *)&Pixel)[0]
1602 + 2.301960784313725357840079e-3f*((uint8_t *)&Pixel)[1]
1603 + 4.470588235294117808150007e-4f*((uint8_t *)&Pixel)[2];
1607 for(y = 0; y < Height; y++, Src += Width)
1608 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1611 DestF[i] = ((uint8_t *)&Pixel)[Order[0]]/255.0f;
1612 DestF[i + ChannelStride] = ((uint8_t *)&Pixel)[Order[1]]/255.0f;
1613 DestF[i + ChannelStride2] = ((uint8_t *)&Pixel)[Order[2]]/255.0f;
1617 for(y = 0; y < Height; y++, Src += Width)
1618 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1621 DestF[i] = ((uint8_t *)&Pixel)[Order[0]]/255.0f;
1622 DestF[i + ChannelStride] = ((uint8_t *)&Pixel)[Order[1]]/255.0f;
1623 DestF[i + ChannelStride2] = ((uint8_t *)&Pixel)[Order[2]]/255.0f;
1624 DestF[i + ChannelStride3] = ((uint8_t *)&Pixel)[Order[3]]/255.0f;
1629 case IMAGEIO_DOUBLE:
1630 if(!(DestD = (
double *)
Malloc(
sizeof(
double)*NumChannels*NumPixels)))
1636 for(y = 0; y < Height; y++, Src += Width)
1637 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1640 DestD[i] = 1.172549019607843070675535e-3*((uint8_t *)&Pixel)[0]
1641 + 2.301960784313725357840079e-3*((uint8_t *)&Pixel)[1]
1642 + 4.470588235294117808150007e-4*((uint8_t *)&Pixel)[2];
1646 for(y = 0; y < Height; y++, Src += Width)
1647 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1650 DestD[i] = ((uint8_t *)&Pixel)[Order[0]]/255.0;
1651 DestD[i + ChannelStride] = ((uint8_t *)&Pixel)[Order[1]]/255.0;
1652 DestD[i + ChannelStride2] = ((uint8_t *)&Pixel)[Order[2]]/255.0;
1656 for(y = 0; y < Height; y++, Src += Width)
1657 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1660 DestD[i] = ((uint8_t *)&Pixel)[Order[0]]/255.0;
1661 DestD[i + ChannelStride] = ((uint8_t *)&Pixel)[Order[1]]/255.0;
1662 DestD[i + ChannelStride2] = ((uint8_t *)&Pixel)[Order[2]]/255.0;
1663 DestD[i + ChannelStride3] = ((uint8_t *)&Pixel)[Order[3]]/255.0;
1675 static uint32_t *ConvertFromFormat(
void *Src,
int Width,
int Height,
1678 const int NumPixels = Width*Height;
1679 const int NumChannels = (Format & IMAGEIO_GRAYSCALE) ?
1680 1 : ((Format & IMAGEIO_STRIP_ALPHA) ? 3 : 4);
1681 const int ChannelStride = (Format & IMAGEIO_PLANAR) ? NumPixels : 1;
1682 const int ChannelStride2 = 2*ChannelStride;
1683 const int ChannelStride3 = 3*ChannelStride;
1684 double *SrcD = (
double *)Src;
1685 float *SrcF = (
float *)Src;
1686 uint8_t *SrcU8 = (uint8_t *)Src;
1687 uint8_t *Dest, *DestPtr;
1688 int Order[4] = {0, 1, 2, 3};
1689 int i, x, y, PixelStride, RowStride;
1692 if(!(Dest = (uint8_t *)
Malloc(
sizeof(uint32_t)*NumPixels)))
1696 PixelStride = (Format & IMAGEIO_PLANAR) ? 1 : NumChannels;
1698 if(Format & IMAGEIO_COLUMNMAJOR)
1700 RowStride = PixelStride;
1701 PixelStride *= Height;
1704 RowStride = Width*PixelStride;
1706 if(Format & IMAGEIO_BGRFLIP)
1712 if((Format & IMAGEIO_AFLIP) && !(Format & IMAGEIO_STRIP_ALPHA))
1714 Order[3] = Order[2];
1715 Order[2] = Order[1];
1716 Order[1] = Order[0];
1720 switch(Format & (IMAGEIO_U8 | IMAGEIO_SINGLE | IMAGEIO_DOUBLE))
1726 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1727 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1731 DestPtr[4*x + 2] = SrcU8[i];
1732 DestPtr[4*x + 3] = 255;
1736 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1737 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1739 DestPtr[4*x + Order[0]] = SrcU8[i];
1740 DestPtr[4*x + Order[1]] = SrcU8[i + ChannelStride];
1741 DestPtr[4*x + Order[2]] = SrcU8[i + ChannelStride2];
1742 DestPtr[4*x + 3] = 255;
1746 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1747 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1749 DestPtr[4*x + Order[0]] = SrcU8[i];
1750 DestPtr[4*x + Order[1]] = SrcU8[i + ChannelStride];
1751 DestPtr[4*x + Order[2]] = SrcU8[i + ChannelStride2];
1752 DestPtr[4*x + Order[3]] = SrcU8[i + ChannelStride3];
1757 case IMAGEIO_SINGLE:
1761 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1762 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1767 DestPtr[4*x + 3] = 255;
1771 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1772 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1775 DestPtr[4*x + Order[1]] =
ROUNDCLAMPF(SrcF[i + ChannelStride]);
1776 DestPtr[4*x + Order[2]] =
ROUNDCLAMPF(SrcF[i + ChannelStride2]);
1777 DestPtr[4*x + 3] = 255;
1781 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1782 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1785 DestPtr[4*x + Order[1]] =
ROUNDCLAMPF(SrcF[i + ChannelStride]);
1786 DestPtr[4*x + Order[2]] =
ROUNDCLAMPF(SrcF[i + ChannelStride2]);
1787 DestPtr[4*x + Order[3]] =
ROUNDCLAMPF(SrcF[i + ChannelStride3]);
1792 case IMAGEIO_DOUBLE:
1796 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1797 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1802 DestPtr[4*x + 3] = 255;
1806 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1807 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1809 DestPtr[4*x + Order[0]] =
ROUNDCLAMP(SrcD[i]);
1810 DestPtr[4*x + Order[1]] =
ROUNDCLAMP(SrcD[i + ChannelStride]);
1811 DestPtr[4*x + Order[2]] =
ROUNDCLAMP(SrcD[i + ChannelStride2]);
1812 DestPtr[4*x + 3] = 255;;
1816 for(y = 0; y < Height; y++, DestPtr += 4*Width)
1817 for(x = 0, i = RowStride*y; x < Width; x++, i += PixelStride)
1819 DestPtr[4*x + Order[0]] =
ROUNDCLAMP(SrcD[i]);
1820 DestPtr[4*x + Order[1]] =
ROUNDCLAMP(SrcD[i + ChannelStride]);
1821 DestPtr[4*x + Order[2]] =
ROUNDCLAMP(SrcD[i + ChannelStride2]);
1822 DestPtr[4*x + Order[3]] =
ROUNDCLAMP(SrcD[i + ChannelStride3]);
1831 return (uint32_t *)Dest;
1852 if(!(File = fopen(FileName,
"rb")))
1856 Magic = ((uint32_t)getc(File));
1857 Magic |= ((uint32_t)getc(File)) << 8;
1858 Magic |= ((uint32_t)getc(File)) << 16;
1859 Magic |= ((uint32_t)getc(File)) << 24;
1870 if((Magic & 0x0000FFFFL) == 0x00004D42L)
1871 strcpy(Type,
"BMP");
1872 else if((Magic & 0x00FFFFFFL) == 0x00FFD8FFL)
1873 strcpy(Type,
"JPEG");
1874 else if(Magic == 0x474E5089L)
1875 strcpy(Type,
"PNG");
1876 else if(Magic == 0x002A4949L || Magic == 0x2A004D4DL)
1877 strcpy(Type,
"TIFF");
1878 else if(Magic == 0x38464947L)
1879 strcpy(Type,
"GIF");
1880 else if(Magic == 0x474E4D8AL)
1881 strcpy(Type,
"MNG");
1882 else if((Magic & 0xF0FF00FFL) == 0x0001000AL
1883 && ((Magic >> 8) & 0xFF) < 6)
1884 strcpy(Type,
"PCX");
1974 const char *FileName,
unsigned Format)
1977 uint32_t *ImageU8 = NULL;
1984 if(!(File = fopen(FileName,
"rb")))
1986 ErrorMessage(
"Unable to open file \"%s\".\n", FileName);
1990 if(!strcmp(Type,
"BMP"))
1992 if(!ReadBmp(&ImageU8, Width, Height, File))
1995 else if(!strcmp(Type,
"JPEG"))
1998 if(!(ReadJpeg(&ImageU8, Width, Height, File)))
2002 "Compile with USE_LIBJPEG to enable JPEG reading.\n",
2006 else if(!strcmp(Type,
"PNG"))
2009 if(!(ReadPng(&ImageU8, Width, Height, File)))
2013 "Compile with USE_LIBPNG to enable PNG reading.\n",
2017 else if(!strcmp(Type,
"TIFF"))
2022 if(!(ReadTiff(&ImageU8, Width, Height, FileName, 0)))
2028 "Compile with USE_LIBTIFF to enable TIFF reading.\n",
2036 ErrorMessage(
"File \"%s\" is a %s image.", FileName, Type);
2038 ErrorMessage(
"File \"%s\" is an unrecognized format.", FileName);
2045 if(ImageU8 && Format)
2047 Image = ConvertToFormat(ImageU8, *Width, *Height, Format);
2077 const char *FileName,
unsigned Format,
int Quality)
2081 enum {BMP_FORMAT, JPEG_FORMAT, PNG_FORMAT, TIFF_FORMAT} FileFormat;
2084 if(!Image || Width <= 0 || Height <= 0)
2091 if(StringEndsWith(FileName,
".bmp"))
2092 FileFormat = BMP_FORMAT;
2093 else if(StringEndsWith(FileName,
".jpg")
2094 || StringEndsWith(FileName,
".jpeg"))
2096 FileFormat = JPEG_FORMAT;
2099 ErrorMessage(
"Compile with USE_LIBJPEG to enable JPEG writing.\n");
2103 else if(StringEndsWith(FileName,
".png"))
2105 FileFormat = PNG_FORMAT;
2108 ErrorMessage(
"Compile with USE_LIBPNG to enable PNG writing.\n");
2112 else if(StringEndsWith(FileName,
".tif")
2113 || StringEndsWith(FileName,
".tiff"))
2115 FileFormat = TIFF_FORMAT;
2118 ErrorMessage(
"Compile with USE_LIBTIFF to enable TIFF writing.\n");
2126 if(StringEndsWith(FileName,
".gif"))
2128 else if(StringEndsWith(FileName,
".mng"))
2130 else if(StringEndsWith(FileName,
".pcx"))
2133 ErrorMessage(
"Unable to determine format from extension.\n");
2139 if(!(File = fopen(FileName,
"wb")))
2141 ErrorMessage(
"Unable to write to file \"%s\".\n", FileName);
2145 if(!(ImageU8 = ConvertFromFormat(Image, Width, Height, Format)))
2151 Success = WriteBmp(ImageU8, Width, Height, File);
2155 Success = WriteJpeg(ImageU8, Width, Height, File, Quality);
2165 Success = WritePng(ImageU8, Width, Height, File);
2171 Success = WriteTiff(ImageU8, Width, Height, FileName);