BMMRES BitmapIO_PNG::Save(const TCHAR *filename, Bitmap *map) { if(!map) return(ProcessImageIOError(&bi,BMMRES_INTERNALERROR)); openMode = BMM_OPEN_W; if((ostream = _tfopen(filename,_T("wb"))) == NULL) return (ProcessImageIOError(&bi)); BitmapStorage *palettedStorage = NULL; png = png_create_write_struct (PNG_VERSION, (void *) this, error_func, warning_func); if (setjmp(png->jmpbuf)) { if (info) for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } if (palettedStorage) delete palettedStorage; fclose(ostream); _tremove(filename); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } info = png_create_info_struct(png); png_init_io(png, ostream); switch(cfg.color_type) { case PngPalette: info->color_type = PNG_COLOR_TYPE_PALETTE; info->pixel_depth = 8; info->valid |= PNG_INFO_PLTE; info->num_palette = 256; break; case PngRGB: info->color_type = PNG_COLOR_TYPE_RGB; break; case PngRGBA: info->color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; case PngGray: info->color_type = PNG_COLOR_TYPE_GRAY; break; case PngGrayA: info->color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; } info->width = map->Width(); info->height = map->Height(); if (OutputGamma() != 1.0f) { info->valid |= PNG_INFO_gAMA; info->gamma = OutputGamma(); } else info->gamma = 1.0f; if (map->Aspect() != 1.0f) { info->valid |= PNG_INFO_pHYs; info->x_pixels_per_unit = (png_uint_32)(1024.0f * map->Aspect()); info->y_pixels_per_unit = 1024; info->phys_unit_type = 0; } if (cfg.interlaced) info->interlace_type = 1; else info->interlace_type = 0; switch( info->color_type) { case PNG_COLOR_TYPE_PALETTE: case PNG_COLOR_TYPE_GRAY: info->channels = 1; break; case PNG_COLOR_TYPE_GRAY_ALPHA: info->channels = 2; break; case PNG_COLOR_TYPE_RGB: info->channels = 3; break; case PNG_COLOR_TYPE_RGB_ALPHA: info->channels = 4; break; } info->bit_depth = cfg.bitdepth; info->rowbytes = info->width * info->channels * info->bit_depth / 8; row_pointers = (png_bytep *)malloc(info->height * sizeof(png_bytep)); for (png_uint_32 i = 0; i < info->height; i++) row_pointers[i] = (png_bytep)malloc(info->rowbytes); switch (info->bit_depth) { case 16: // this is only RGB/RGBA/Gray/GrayA switch(info->color_type) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: { BMM_Color_64 *line64 = (BMM_Color_64 *) calloc(info->width,sizeof(BMM_Color_64)); for (png_uint_32 iy = 0; iy < info->height; ++iy) { if (GetOutputPixels(0, iy, info->width, line64) != 1) { for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } fclose(ostream); _tremove(filename); free(line64); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } BMM_Color_64 *l64=line64; unsigned short *oshort = (unsigned short *)row_pointers[iy]; for (png_uint_32 ix = 0; ix < info->width; ++l64, ix++) { *oshort = (unsigned short)l64->r; oshort++; *oshort = (unsigned short)l64->g; oshort++; *oshort = (unsigned short)l64->b; oshort++; if (info->channels == 4) { *oshort = (unsigned short)l64->a; oshort++; } } } free(line64); } break; case PNG_COLOR_TYPE_GRAY: { for (png_uint_32 iy = 0; iy < info->height; ++iy) if (map->Get16Gray(0, iy, info->width, (unsigned short *)row_pointers[iy]) != 1) { for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } fclose(ostream); _tremove(filename); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } } break; case PNG_COLOR_TYPE_GRAY_ALPHA: { BMM_Color_64 *line64 = (BMM_Color_64 *) calloc(info->width, sizeof(BMM_Color_64)); unsigned short *line = (unsigned short *) calloc(info->width, sizeof(unsigned short)); for (png_uint_32 iy = 0; iy < info->height; ++iy) { if (GetOutputPixels(0, iy, info->width, line64) != 1 || map->Get16Gray(0, iy, info->width, line) != 1) { for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } free(line64); free(line); fclose(ostream); _tremove(filename); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } BMM_Color_64 *l64 = line64; unsigned short *l=line; unsigned short *oshort = (unsigned short *)row_pointers[iy]; for (png_uint_32 ix = 0; ix < info->width; ix++, l64++) { *oshort++ = *l++; *oshort++ = (unsigned short)l64->a; } } free(line64); free(line); } break; } break; case 8: // this can be any type switch(info->color_type) { case PNG_COLOR_TYPE_PALETTE: { // Set up a palette buffer PixelBuf48 palettebuf(info->num_palette); BMM_Color_48 *pal = palettebuf.Ptr(); // Must compute a color palette, and reduce the image to 256 colors! // this calculates a palette based on the gamma corrected values, which // corresponds to what GetOutputPixels returns. if(CalcOutputPalette(256, pal) == 0) { for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } fclose(ostream); _tremove(filename); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } info->palette = (png_color *)malloc(info->num_palette * sizeof(png_color)); for (int i = 0; i < info->num_palette; i++) { info->palette[i].red = (unsigned char)(pal[i].r >> 8); info->palette[i].green = (unsigned char)(pal[i].g >> 8); info->palette[i].blue = (unsigned char)(pal[i].b >> 8); } PixelBuf64 line(info->width); ColorPacker* cpack = BMMNewColorPacker(info->width, pal, info->num_palette); for (png_uint_32 iy=0; iy < info->height; ++iy) { if(!GetOutputPixels(0, iy, info->width, line.Ptr())) { for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } fclose(ostream); _tremove(filename); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } cpack->PackLine(line.Ptr(), row_pointers[iy], info->width); } cpack->DeleteThis(); } break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: { BMM_Color_32 *line32 = (BMM_Color_32 *) calloc(info->width,sizeof(BMM_Color_32)); for (png_uint_32 iy = 0; iy < info->height; ++iy) { if (GetDitheredOutputPixels(0, iy, info->width, line32) != 1) { for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } fclose(ostream); _tremove(filename); free(line32); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } BMM_Color_32 *l32=line32; unsigned char *obyte = (unsigned char *)row_pointers[iy]; for (png_uint_32 ix = 0; ix < info->width; ++l32, ix++) { *obyte = (unsigned char)l32->r; obyte++; *obyte = (unsigned char)l32->g; obyte++; *obyte = (unsigned char)l32->b; obyte++; if (info->channels == 4) { *obyte = (unsigned char)l32->a; obyte++; } } } free(line32); } break; case PNG_COLOR_TYPE_GRAY: { unsigned short *line = (unsigned short *) calloc(info->width * info->channels, sizeof(unsigned short)); for (png_uint_32 iy = 0; iy < info->height; ++iy) { if (map->Get16Gray(0, iy, info->width, line) != 1) { for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } fclose(ostream); _tremove(filename); free(line); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } unsigned short *l=line; unsigned char *obyte = (unsigned char *)row_pointers[iy]; for (png_uint_32 ix = 0; ix < info->width; ix++) { *obyte++ = (unsigned char)(*l >> 8); l++; } } free(line); } break; case PNG_COLOR_TYPE_GRAY_ALPHA: { BMM_Color_64 *line64 = (BMM_Color_64 *) calloc(info->width,sizeof(BMM_Color_64)); unsigned short *line = (unsigned short *) calloc(info->width, sizeof(unsigned short)); for (png_uint_32 iy = 0; iy < info->height; ++iy) { if (GetOutputPixels(0, iy, info->width, line64) != 1 || map->Get16Gray(0, iy, info->width, line) != 1) { for (png_uint_32 i = 0; i < info->height; i++) if (row_pointers[i]) free(row_pointers[i]); if (row_pointers) { free(row_pointers); row_pointers = NULL; } fclose(ostream); _tremove(filename); free(line); free(line64); png_destroy_write_struct (&png, &info); return BMMRES_IOERROR; } unsigned short *l=line; BMM_Color_64 *l64 = line64; unsigned char *obyte = (unsigned char *)row_pointers[iy]; for (png_uint_32 ix = 0; ix < info->width; ix++, l64++) { *obyte++ = (unsigned char)(*l >> 8); l++; *obyte++ = (unsigned char)(l64->a >> 8); } } free(line); free(line64); } break; } break; #ifdef OUTPUT_1_2_4 case 4: { // Paletted only } break; case 2: { // Paletted only } break; case 1: { // Paletted only } #endif break; } png_write_info(png, info); png_set_swap(png); png_write_image(png, row_pointers); png_write_end(png, info); fclose(ostream); for (i = 0; i < info->height; i++) free(row_pointers[i]); free(row_pointers); png_destroy_write_struct (&png, &info); return BMMRES_SUCCESS; }
BMMRES BitmapIO_BMP::Write(int frame) { BMMRES result = BMMRES_SUCCESS; //-- If we haven't gone through an OpenOutput(), leave if (openMode != BMM_OPEN_W) return (ProcessImageIOError(&bi,BMMRES_INTERNALERROR)); //-- Resolve Filename -------------------------------- TCHAR filename[MAX_PATH]; if (frame == BMM_SINGLEFRAME) { _tcscpy(filename,bi.Name()); } else { if (!BMMCreateNumberedFilename(bi.Name(),frame,filename)) return (ProcessImageIOError(&bi,BMMRES_NUMBEREDFILENAMEERROR)); } //-- Create Image File ------------------------------- File file(filename, _T("wb")); if (!file.stream) return (ProcessImageIOError(&bi)); //-- Create File Header------------------------------- BITMAPFILEHEADER hdr; PBITMAPINFO pbmi; int lx,y; memset(&hdr,0,sizeof(BITMAPFILEHEADER)); hdr.bfType = 0x4d42; hdr.bfOffBits = sizeof(BITMAPINFOHEADER) + sizeof(BITMAPFILEHEADER); //-- Pallette buffer and pixel buffer for 8bit output PixelBuf8 *pixBuf; BYTE *pix; PixelBuf48 *palBuf; BMM_Color_48 *pal; int w = map->Width(); int wb = (map->Width() + 3) & ~3; // width must be multiple of 4 int h = map->Height(); switch(mParams.outDepth) { //-- Paletted BMP required case BMM_PALETTED: pixBuf = new PixelBuf8(wb*h); palBuf = new PixelBuf48(256); if( (!pixBuf) || (!palBuf) ) ProcessImageIOError(&bi,BMMRES_MEMORYERROR); pix = pixBuf->Ptr(); pal = palBuf->Ptr(); if( (!pix) || (!pal) ) ProcessImageIOError(&bi,BMMRES_MEMORYERROR); if( Storage()->Paletted()) { //-- Existing map is palletted...so get the pallete and //-- the look up table..we are done.. Storage()->GetPalette(0, 256, pal); for(y = 0; y < h; y++) Storage()->GetIndexPixels(0,(h-y-1),w,pix+wb*y); } else { //-- Caluculate the pallete for the image.. //-- Then create the look up table if(CalcOutputPalette(256,pal) == 0) ProcessImageIOError(&bi); PixelBuf64 line(w); ColorPacker* cPack = BMMNewColorPacker(w,pal,256); for(y=0; y<h; y++) { if(!GetOutputPixels(0,(h-y-1),w,line.Ptr())) ProcessImageIOError(&bi); cPack->PackLine(line.Ptr(),pix+y*wb,w); } cPack->DeleteThis(); } //-- Fill in the BITMAPINFO structure--------------------- lx = sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD) + wb*h; pbmi = (PBITMAPINFO)LocalAlloc(LPTR,lx); if (!pbmi) return (ProcessImageIOError(&bi,GetString(IDS_CONVERT_ERROR))); memset(pbmi,0,lx); pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); pbmi->bmiHeader.biWidth = w; pbmi->bmiHeader.biHeight = h; pbmi->bmiHeader.biPlanes = 1; pbmi->bmiHeader.biBitCount = 8; pbmi->bmiHeader.biCompression = BI_RGB; pbmi->bmiHeader.biSizeImage = w*h; //256*sizeof(RGBQUAD) + wb*h; pbmi->bmiHeader.biXPelsPerMeter = 2834; pbmi->bmiHeader.biYPelsPerMeter = 2834; hdr.bfOffBits += 256*sizeof(RGBQUAD); // DS 2/16/98 RGBQUAD *rgb; //-- Fill in the palette rgb = (RGBQUAD*) &(pbmi->bmiColors[0]); for(y=0; y<256; y++) { rgb->rgbRed = pal[y].r >> 8; rgb->rgbGreen = pal[y].g >> 8; rgb->rgbBlue = pal[y].b >> 8; rgb++; } //-- Fill in the look up table memcpy((LPBYTE)rgb, pix,wb*h); break; //-- RGB24 requested-------------------------------------- case BMM_NO_TYPE: case BMM_TRUE_24: { //-- Convert Bitmap to DIB --------------------------- pbmi = GetDitheredOutputDib(); if (!pbmi) return (ProcessImageIOError(&bi,GetString(IDS_CONVERT_ERROR))); //-- Prepare Header ------------------------ if (bi.GetUpdateWindow()) SendMessage(bi.GetUpdateWindow(),BMM_PROGRESS,25,100); int rb = (map->Width() * 3 + 3) & ~3; // must be multiple of 4 bytes lx = sizeof(BITMAPINFOHEADER) + (rb * map->Height()); } break; default: assert(0); return BMMRES_IOERROR; break; } hdr.bfSize = lx + sizeof(BITMAPFILEHEADER); //-- Write Header ------------------------ size_t res = fwrite(&hdr,1,sizeof(BITMAPFILEHEADER),file.stream); if (res != sizeof(BITMAPFILEHEADER)) { io_error: result = ProcessImageIOError(&bi); LocalFree(pbmi); return (result); } //-- Write Image File -------------------------------- if (bi.GetUpdateWindow()) SendMessage(bi.GetUpdateWindow(),BMM_PROGRESS,50,100); res = fwrite(pbmi,1,lx,file.stream); if (res != lx) goto io_error; LocalFree(pbmi); if (bi.GetUpdateWindow()) SendMessage(bi.GetUpdateWindow(),BMM_PROGRESS,100,100); return (result); }