/* Read an PNG file, returning the storage where it's located */ BitmapStorage * BitmapIO_PNG::ReadPNGFile(BitmapInfo *fbi, BitmapManager *manager) { BitmapStorage *storage = NULL; unsigned char magic_numbers[8]; if((istream=_tfopen(fbi->Name(), _T("rb")))==NULL) return NULL; // grab the first 8 bytes for testing if (fread(magic_numbers, 1, 8, istream) != 8) { fclose(istream); return NULL; } else rewind(istream); // Make sure we're a png file if (!png_check_sig(magic_numbers, 8)) { fclose(istream); return NULL; } png = png_create_read_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 (storage) { delete storage; storage = NULL; } fclose(istream); png_destroy_read_struct (&png, &info, NULL); return NULL; } info = png_create_info_struct(png); png_init_io(png, istream); png_read_info(png, info); fbi->SetWidth((WORD)info->width); fbi->SetHeight((WORD)info->height); if (info->valid & PNG_INFO_gAMA) fbi->SetGamma(info->gamma); // else // fbi->SetGamma (1.0f); if (info->valid & PNG_INFO_pHYs) fbi->SetAspect((float)info->x_pixels_per_unit / (float)info->y_pixels_per_unit); else fbi->SetAspect(1.0f); fbi->SetFlags(0); /* expand grayscale images to the full 8 bits */ /* expand images with transparency to full alpha channels */ /* I'm going to ignore lineart and just expand it to 8 bits */ if ((info->color_type == PNG_COLOR_TYPE_PALETTE && info->bit_depth < 8) || (info->color_type == PNG_COLOR_TYPE_GRAY && info->bit_depth < 8) || (info->valid & PNG_INFO_tRNS)) png_set_expand(png); int number_passes = 1; if (info->interlace_type) number_passes = png_set_interlace_handling(png); if (info->bit_depth == 16) png_set_swap(png); png_read_update_info(png, info); int bmtype = BMM_NO_TYPE; if (info->bit_depth == 1) { bmtype = BMM_LINE_ART; } else { switch(info->color_type) { case PNG_COLOR_TYPE_PALETTE: bmtype = BMM_PALETTED; break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: switch(info->bit_depth) { case 2: case 4: // Not allowed break; case 8: bmtype = BMM_TRUE_32; // zero alpha for those that don't have it break; case 16: bmtype = BMM_TRUE_64; break; } break; case PNG_COLOR_TYPE_GRAY_ALPHA: case PNG_COLOR_TYPE_GRAY: switch(info->bit_depth) { case 2: case 4: // we should never get here because of the expand code so drop through break; case 8: bmtype = BMM_GRAY_8; break; case 16: bmtype = BMM_GRAY_16; break; } break; } } if (bmtype == BMM_NO_TYPE) { fclose(istream); png_destroy_read_struct (&png, &info, NULL); return NULL; } // Create a storage for this bitmap... // (we may need to special case GRAY since it has a problem with alpha) if(info->color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { if (info->bit_depth == 16) { storage = BMMCreateStorage(manager, BMM_TRUE_64); fbi->SetType(BMM_TRUE_64); } else { storage = BMMCreateStorage(manager, BMM_TRUE_32); fbi->SetType(BMM_TRUE_32); } } else { storage = BMMCreateStorage(manager, bmtype); fbi->SetType(bmtype); } if (info->channels == 2 || info->channels == 4) fbi->SetFlags(MAP_HAS_ALPHA); if(storage == NULL) { fclose(istream); png_destroy_read_struct (&png, &info, NULL); return NULL; } if(storage->Allocate(fbi, manager, BMM_OPEN_R)==0) { delete storage; storage = NULL; fclose(istream); png_destroy_read_struct (&png, &info, NULL); return NULL; } 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); // now read the image png_read_image(png, row_pointers); switch(bmtype) { case BMM_LINE_ART: { BMM_Color_64 *line64 = (BMM_Color_64 *) calloc(info->width, sizeof(BMM_Color_64)); for (png_uint_32 iy = 0; iy < info->height; iy++) { BMM_Color_64 *l64 = line64; for (png_uint_32 ix = 0; ix < info->width; ix++,l64++) { png_uint_32 abyte = ix / 8; png_uint_32 abit = ix % 8; unsigned char tbyte = row_pointers[iy][abyte]; unsigned char c = tbyte & (0x80 >> abit); l64->r = l64->g = l64->b = c ? 0xffff : 0; l64->a = 0; } storage->PutPixels(0, iy, info->width, line64); } free(line64); } break; case BMM_PALETTED: { if (info->bit_depth == 8) { for (png_uint_32 iy = 0; iy < info->height; iy++) storage->PutIndexPixels(0, iy, info->width, row_pointers[iy]); } else { unsigned char *pixels = (unsigned char *)calloc(info->width, sizeof(unsigned char)); for (png_uint_32 iy = 0; iy < info->height; iy++) { // now fill a row of pixels unsigned char *inbyte = row_pointers[iy]; for (png_uint_32 ix = 0; ix < info->width; inbyte++) { switch(info->bit_depth) { case 2: pixels[ix] = (*inbyte & 0xc0) >> 6; ix++; if (ix >= info->width) break; pixels[ix] = (*inbyte & 0x30) >> 4; ix++; if (ix >= info->width) break; pixels[ix] = (*inbyte & 0x0c) >> 2; ix++; if (ix >= info->width) break; pixels[ix] = *inbyte & 0x03; ix++; break; case 4: pixels[ix] = (*inbyte & 0xf0) >> 4; ix++; if (ix >= info->width) break; pixels[ix] = *inbyte & 0x0f; ix++; break; } } storage->PutIndexPixels(0, iy, info->width, pixels); } free(pixels); } // Now the palette PixelBuf48 palette(256); BMM_Color_48 *palout = palette.Ptr(); for(int i = 0; i < png->num_palette; ++i,++palout) { palout->r = (USHORT)png->palette[i].red << 8; palout->g = (USHORT)png->palette[i].green << 8; palout->b = (USHORT)png->palette[i].blue << 8; } storage->SetPalette(0, png->num_palette, palette.Ptr()); } break; case BMM_TRUE_32: { BMM_Color_64 *line64 = (BMM_Color_64 *) calloc(info->width, sizeof(BMM_Color_64)); for (png_uint_32 iy = 0; iy < info->height; iy++) { BMM_Color_64 *l64 = line64; for (png_uint_32 ix = 0; ix < info->rowbytes; l64++) { l64->r = (unsigned short) (row_pointers[iy][ix++]) << 8; l64->g = (unsigned short) (row_pointers[iy][ix++]) << 8; l64->b = (unsigned short) (row_pointers[iy][ix++]) << 8; if (info->channels == 4) { l64->a = (unsigned short) (row_pointers[iy][ix++]) << 8; } else l64->a = 0; } storage->PutPixels(0, iy, info->width, line64); } free(line64); } break; case BMM_TRUE_64: { BMM_Color_64 *line64 = (BMM_Color_64 *) calloc(info->width, sizeof(BMM_Color_64)); for (png_uint_32 iy = 0; iy < info->height; iy++) { BMM_Color_64 *l64 = line64; unsigned short *row = (unsigned short *) row_pointers[iy]; for (png_uint_32 ix = 0; ix < info->width; ix++, l64++) { l64->r = *row++; l64->g = *row++; l64->b = *row++; if (info->channels == 4) { l64->a = *row++; } else l64->a = 0; } storage->PutPixels(0, iy, info->width, line64); } free(line64); } break; case BMM_GRAY_8: { BMM_Color_64 *line64 = (BMM_Color_64 *) calloc(info->width, sizeof(BMM_Color_64)); for (png_uint_32 iy = 0; iy < info->height; iy++) { BMM_Color_64 *l64 = line64; for (png_uint_32 ix = 0; ix < info->rowbytes; l64++) { l64->r = l64->g = l64->b = (unsigned short) (row_pointers[iy][ix++]) << 8; if (info->channels == 2) l64->a = (unsigned short) (row_pointers[iy][ix++]) << 8; else l64->a = 0; } storage->PutPixels(0, iy, info->width, line64); } free(line64); } break; case BMM_GRAY_16: { BMM_Color_64 *line64 = (BMM_Color_64 *) calloc(info->width, sizeof(BMM_Color_64)); for (png_uint_32 iy = 0; iy < info->height; iy++) { BMM_Color_64 *l64 = line64; unsigned short *row = (unsigned short *) row_pointers[iy]; for (png_uint_32 ix = 0; ix < info->width; ix++, l64++) { l64->r = l64->g = l64->b = *row++; if (info->channels == 2) { l64->a = *row++; } else l64->a = 0; } storage->PutPixels(0, iy, info->width, line64); } free(line64); } break; } png_read_end(png, info); for (i = 0; i < info->height; i++) free(row_pointers[i]); free(row_pointers); fclose(istream); png_destroy_read_struct (&png, &info, NULL); return storage; }
BitmapStorage *BitmapIO_BMP::Load(BitmapInfo *fbi, Bitmap *map, BMMRES *status) { RGBQUAD *rgb = NULL; BMM_Color_48 *pal = NULL; BitmapStorage *s = NULL; BMM_Color_64 *b = NULL; BYTE *p = NULL; BYTE *b8 = NULL; BYTE *b4 = NULL; INT_PTR pixels = 0; int rows = 0; int w = 0; int wb = 0; int h = 0; int j; //-- Initialize Status Optimistically *status = BMMRES_SUCCESS; //-- Make sure nothing weird is going on if(openMode != BMM_NOT_OPEN) { *status = ProcessImageIOError(fbi,BMMRES_INTERNALERROR); return NULL; } //-- Open BMP File ----------------------------------- File file(fbi->Name(), _T("rb")); if (!file.stream) { *status = ProcessImageIOError(fbi); return(NULL); } //-- Read File Header -------------------------------- if (!ReadBimpHeader(file.stream)) { *status = ProcessImageIOError(fbi,BMMRES_BADFILEHEADER); return (NULL); } //-- Update Bitmap Info ------------------------------ fbi->SetWidth( (WORD)bmi.biWidth ); fbi->SetHeight((WORD)bmi.biHeight); if ((bmi.biBitCount != 32 && bmi.biBitCount != 24 && bmi.biBitCount != 8 && bmi.biBitCount != 4) || bmi.biCompression != BI_RGB) { *status = ProcessImageIOError(fbi,GetString(IDS_UNSUPPORTED)); return(NULL); } // fbi->SetGamma(1.0f); fbi->SetAspect(1.0f); switch(bmi.biBitCount) { case 32: fbi->SetType(BMM_TRUE_32); // [Kai@8/19/2008] We need to set this flag so that the alpha storage will be created in BMMCreateStorage fbi->SetFlags(MAP_HAS_ALPHA); break; case 24: fbi->SetType(BMM_TRUE_24); break; case 8: case 4: //-- We don't have a 4 bit bitmap storage anyway. //-- So force 4 bit to 8 bit fbi->SetType(BMM_PALETTED); break; } fbi->SetFirstFrame(0); fbi->SetLastFrame(0); //-- Create Image Storage ---------------------------- switch(bmi.biBitCount) { case 32: s = BMMCreateStorage(map->Manager(), BMM_TRUE_32); break; case 24: s = BMMCreateStorage(map->Manager(),BMM_TRUE_32); break; case 8: case 4: //-- We don't have a 4 bit bitmap storage anyway. //-- So force 4 bit storage to 8 bit storage s = BMMCreateStorage(map->Manager(),BMM_PALETTED); break; } if(!s) { *status = ProcessImageIOError(fbi,BMMRES_CANTSTORAGE); return NULL; } //-- Allocate Image Storage -------------------------- if (s->Allocate(fbi,map->Manager(),BMM_OPEN_R)==0) { memory_error_out: *status = ProcessImageIOError(fbi,BMMRES_MEMORYERROR); goto bail_out; io_error_out: *status = ProcessImageIOError(fbi); bail_out: if (s) delete s; if (b) free(b); if (p) free(p); if (rgb) free(rgb); if (pal) free(pal); return NULL; } switch(bmi.biBitCount) { case 4: //-- Read 4 bit Palette ------------------------------------ if (!bmi.biClrUsed) bmi.biClrUsed = 16; rgb = (RGBQUAD *)malloc(bmi.biClrUsed * sizeof(RGBQUAD)); if (!rgb) goto memory_error_out; pal = (BMM_Color_48 *)malloc(bmi.biClrUsed * sizeof(BMM_Color_48)); if (!pal) goto memory_error_out; if (fread(rgb,sizeof(RGBQUAD),bmi.biClrUsed,file.stream) != bmi.biClrUsed) goto io_error_out; for (j = 0; j < (int)bmi.biClrUsed; j++) { pal[j].r = rgb[j].rgbRed << 8; pal[j].g = rgb[j].rgbGreen << 8; pal[j].b = rgb[j].rgbBlue << 8; } s->SetPalette(0,bmi.biClrUsed,pal); free(pal); free(rgb); pal = NULL; rgb = NULL; //-- Read Image (4 Bits) ----------------------------- w = fbi->Width(); wb = ( ((fbi->Width()+1)/2)+ 3) & ~3; // width must be multiple of 4 h = fbi->Height() - 1; p = (BYTE *)malloc(wb); b4 = (BYTE *)malloc(w); if (!p || !b4) goto memory_error_out; do { pixels = fread(p,1,wb,file.stream); if (pixels != wb && pixels != 0) goto io_error_out; if (pixels) { // -- the 4bit buffer p has two pixels per byte. // -- convert it to 8 bit buffer b8 that has one pixel per byte for(j=0;j<w;j++) { b4[j] = (j%2) ? (p[j/2] & 0x0f) : (p[j/2] >> 4); } s->PutIndexPixels(0,(h - rows),w,b4); rows++; if (rows>h) break; } //-- Progress Report if (fbi->GetUpdateWindow()) SendMessage(fbi->GetUpdateWindow(),BMM_PROGRESS,rows,h); } while (pixels); break; case 8: //-- Read 8 bitPalette ------------------------------------ if (!bmi.biClrUsed) bmi.biClrUsed = 256; rgb = (RGBQUAD *)malloc(bmi.biClrUsed * sizeof(RGBQUAD)); if (!rgb) goto memory_error_out; pal = (BMM_Color_48 *)malloc(bmi.biClrUsed * sizeof(BMM_Color_48)); if (!pal) goto memory_error_out; if (fread(rgb,sizeof(RGBQUAD),bmi.biClrUsed,file.stream) != bmi.biClrUsed) goto io_error_out; for ( j = 0; j < (int)bmi.biClrUsed; j++) { pal[j].r = rgb[j].rgbRed << 8; pal[j].g = rgb[j].rgbGreen << 8; pal[j].b = rgb[j].rgbBlue << 8; } s->SetPalette(0,bmi.biClrUsed,pal); free(pal); free(rgb); pal = NULL; rgb = NULL; //-- Read Image (8 Bits) ----------------------------- w = (fbi->Width() + 3) & ~3; // width must be multiple of 4 h = fbi->Height() - 1; p = (BYTE *)malloc(w); if (!p) goto memory_error_out; do { pixels = fread(p,1,w,file.stream); if (pixels != w && pixels != 0) goto io_error_out; if (pixels) { s->PutIndexPixels(0,(h - rows),fbi->Width(),p); rows++; if (rows>h) break; } //-- Progress Report if (fbi->GetUpdateWindow()) SendMessage(fbi->GetUpdateWindow(),BMM_PROGRESS,rows,h); } while (pixels); break; case 24: case 32: { //-- Read Image (24/32 Bits) ---------------------------- bool hasAlpha = (bmi.biBitCount == 32); w = fbi->Width(); if(!hasAlpha) { wb = (fbi->Width() * 3 + 3) & ~3; // width bytes must be multiple of 4 } else { wb = (fbi->Width() * 4); } h = fbi->Height() - 1; b = (BMM_Color_64 *)malloc(fbi->Width()*sizeof(BMM_Color_64)); p = (BYTE *)malloc(wb); if(!b || !p) goto memory_error_out; BYTE *ptr; do { pixels = fread(p,1,wb,file.stream); if (pixels != wb && pixels != 0) goto io_error_out; if (pixels) { ptr = p; for (int x = 0; x < w; x++) { b[x].b = (WORD)((*ptr++) << 8); b[x].g = (WORD)((*ptr++) << 8); b[x].r = (WORD)((*ptr++) << 8); if(hasAlpha) { b[x].a = (WORD)((*ptr++) << 8); } } if (s->PutPixels(0,(h - rows),w,b)!=1) goto io_error_out; rows++; if (rows>h) break; } //-- Progress Report if (fbi->GetUpdateWindow()) SendMessage(fbi->GetUpdateWindow(),BMM_PROGRESS,rows,h); } while (pixels); } break; } //-- Clean Up ---------------------------------------- if (b) free(b); if (p) free(p); if (b8)free(b8); if (b4)free(b4); //-- Set the storage's BitmapInfo s->bi.CopyImageInfo(fbi); return s; }