static int png2vips_image( Read *read, VipsImage *out ) { int interlace_type = png_get_interlace_type( read->pPng, read->pInfo ); VipsImage **t = (VipsImage **) vips_object_local_array( VIPS_OBJECT( out ), 3 ); if( interlace_type != PNG_INTERLACE_NONE ) { /* Arg awful interlaced image. We have to load to a huge mem * buffer, then copy to out. */ t[0] = vips_image_new_memory(); if( png2vips_header( read, t[0] ) || png2vips_interlace( read, t[0] ) || vips_image_write( t[0], out ) ) return( -1 ); } else { t[0] = vips_image_new(); if( png2vips_header( read, t[0] ) || vips_image_generate( t[0], NULL, png2vips_generate, NULL, read, NULL ) || vips_sequential( t[0], &t[1], "tile_height", 8, "access", read->readbehind ? VIPS_ACCESS_SEQUENTIAL : VIPS_ACCESS_SEQUENTIAL_UNBUFFERED, NULL ) || vips_image_write( t[1], out ) ) return( -1 ); } return( 0 ); }
void pngSetHeader(PngStream* stream) { stream->info.imageWidth = png_get_image_width(stream->png_ptr, stream->info_ptr); stream->info.imageHeight = png_get_image_height(stream->png_ptr, stream->info_ptr); stream->info.numComponents = png_get_channels(stream->png_ptr, stream->info_ptr); stream->info.colorSpace = getPngDecColourType(png_get_color_type(stream->png_ptr, stream->info_ptr)); stream->info.bitDepth = png_get_bit_depth(stream->png_ptr, stream->info_ptr); stream->info.interlaceMethod = png_get_interlace_type(stream->png_ptr, stream->info_ptr); stream->info.chunkInformation = pngDecGetChunkInformation(stream); }
void oil_libpng_read_scanline(struct oil_libpng *ol, unsigned char *outbuf) { switch (png_get_interlace_type(ol->rpng, ol->rinfo)) { case PNG_INTERLACE_NONE: read_scanline(ol); break; case PNG_INTERLACE_ADAM7: read_scanline_interlaced(ol); break; } oil_scale_out(&ol->os, outbuf); }
int image_png_read_header(MediaScanImage *i, MediaScanResult *r) { int x; PNGData *p = malloc(sizeof(PNGData)); i->_png = (void *)p; LOG_MEM("new PNGData @ %p\n", i->_png); p->buf = (Buffer *)r->_buf; p->fp = r->_fp; p->path = r->path; p->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp) p, image_png_error, image_png_warning); if (!p->png_ptr) FATAL("Could not initialize libpng\n"); p->info_ptr = png_create_info_struct(p->png_ptr); if (!p->info_ptr) { png_destroy_read_struct(&p->png_ptr, (png_infopp) NULL, (png_infopp) NULL); FATAL("Could not initialize libpng\n"); } if (setjmp(png_jmpbuf(p->png_ptr))) { image_png_destroy(i); return 0; } png_set_read_fn(p->png_ptr, p, image_png_read_buf); png_read_info(p->png_ptr, p->info_ptr); i->width = png_get_image_width(p->png_ptr, p->info_ptr); i->height = png_get_image_height(p->png_ptr, p->info_ptr); i->channels = png_get_channels(p->png_ptr, p->info_ptr); i->has_alpha = 1; r->mime_type = MIME_IMAGE_PNG; // Match with DLNA profile // DLNA does not support interlaced images if (png_get_interlace_type(p->png_ptr, p->info_ptr) == PNG_INTERLACE_NONE) { for (x = 0; png_profiles_mapping[x].profile; x++) { if (i->width <= png_profiles_mapping[x].max_width && i->height <= png_profiles_mapping[x].max_height) { r->dlna_profile = png_profiles_mapping[x].profile->id; break; } } } return 1; }
int oil_libpng_init(struct oil_libpng *ol, png_structp rpng, png_infop rinfo, int out_width, int out_height) { int ret, in_width, in_height, buf_len; enum oil_colorspace cs; ol->rpng = rpng; ol->rinfo = rinfo; ol->in_vpos = 0; ol->inbuf = NULL; ol->inimage = NULL; cs = png_cs_to_oil(png_get_color_type(rpng, rinfo)); if (cs == OIL_CS_UNKNOWN) { return -1; } in_width = png_get_image_width(rpng, rinfo); in_height = png_get_image_height(rpng, rinfo); ret = oil_scale_init(&ol->os, in_height, out_height, in_width, out_width, cs); if (ret!=0) { free(ol->inbuf); return ret; } buf_len = png_get_rowbytes(rpng, rinfo); switch (png_get_interlace_type(rpng, rinfo)) { case PNG_INTERLACE_NONE: ol->inbuf = malloc(buf_len); if (!ol->inbuf) { oil_scale_free(&ol->os); return -2; } break; case PNG_INTERLACE_ADAM7: ol->inimage = alloc_full_image_buf(in_height, buf_len); if (!ol->inimage) { oil_scale_free(&ol->os); return -2; } png_read_image(rpng, ol->inimage); break; } return 0; }
/* Interlaced PNGs need to be entirely decompressed into memory then can be * served partially from there. Non-interlaced PNGs may be read sequentially. */ gboolean vips__png_isinterlaced( const char *filename ) { VipsImage *image; Read *read; int interlace_type; image = vips_image_new(); if( !(read = read_new_filename( image, filename, FALSE )) ) { g_object_unref( image ); return( -1 ); } interlace_type = png_get_interlace_type( read->pPng, read->pInfo ); g_object_unref( image ); return( interlace_type != PNG_INTERLACE_NONE ); }
/* Interlaced PNGs need to be entirely decompressed into memory then can be * served partially from there. Non-interlaced PNGs may be read sequentially. */ gboolean vips__png_isinterlaced_buffer( const void *buffer, size_t length ) { VipsImage *image; Read *read; int interlace_type; image = vips_image_new(); if( !(read = read_new_buffer( image, buffer, length, TRUE )) ) { g_object_unref( image ); return( -1 ); } interlace_type = png_get_interlace_type( read->pPng, read->pInfo ); g_object_unref( image ); return( interlace_type != PNG_INTERLACE_NONE ); }
void png_row_callback(png_structp png_ptr, png_bytep new_row, png_uint_32 row_num, int pass) { struct cached_image *cimg; #ifdef REPACK_16 unsigned char *tmp; int channels; #endif /* #ifdef REPACK_16 */ cimg=global_cimg; #ifdef REPACK_16 if (cimg->buffer_bytes_per_pixel>4) { channels=cimg->buffer_bytes_per_pixel/sizeof(unsigned short); if (PNG_INTERLACE_NONE==png_get_interlace_type(png_ptr, ((struct png_decoder *)cimg->decoder)->info_ptr)) { unsigned_short_from_2char((unsigned short *)(cimg->buffer+cimg ->buffer_bytes_per_pixel *cimg->width *row_num), new_row, cimg->width *channels); }else{ if ((unsigned)cimg->width > MAXINT / 2 / channels) overalloc(); tmp=mem_alloc(cimg->width*2*channels); a2char_from_unsigned_short(tmp, (unsigned short *)(cimg->buffer +cimg->buffer_bytes_per_pixel *cimg->width*row_num), cimg->width*channels); png_progressive_combine_row(png_ptr, tmp, new_row); unsigned_short_from_2char((unsigned short *)(cimg->buffer +cimg->buffer_bytes_per_pixel *cimg->width*row_num), tmp, cimg->width*channels); mem_free(tmp); } }else #endif /* #ifdef REPACK_16 */ { png_progressive_combine_row(png_ptr, cimg->buffer+cimg->buffer_bytes_per_pixel *cimg->width*row_num, new_row); } cimg->rows_added=1; }
/** * info_callback -- PNG header has been completely received, prepare to process * image data */ static void info_callback(png_structp png_s, png_infop info) { int interlace; png_uint_32 width, height; nspng_content *png_c = png_get_progressive_ptr(png_s); width = png_get_image_width(png_s, info); height = png_get_image_height(png_s, info); interlace = png_get_interlace_type(png_s, info); png_c->base.width = width; png_c->base.height = height; png_c->base.size += width * height * 4; /* see if progressive-conversion should continue */ if (image_cache_speculate((struct content *)png_c) == false) { longjmp(png_jmpbuf(png_s), CBERR_NOPRE); } /* Claim the required memory for the converted PNG */ png_c->bitmap = bitmap_create(width, height, BITMAP_NEW); if (png_c->bitmap == NULL) { /* Failed to create bitmap skip pre-conversion */ longjmp(png_jmpbuf(png_s), CBERR_NOPRE); } png_c->rowstride = bitmap_get_rowstride(png_c->bitmap); png_c->bpp = bitmap_get_bpp(png_c->bitmap); nspng_setup_transforms(png_s, info); png_c->rowbytes = png_get_rowbytes(png_s, info); png_c->interlace = (interlace == PNG_INTERLACE_ADAM7); LOG(("size %li * %li, rowbytes %zu", (unsigned long)width, (unsigned long)height, png_c->rowbytes)); }
/* For little endian systems, ARGB is equivalent to the int32 BGRA. * So, to read the image as RGB */ static SLang_Array_Type *read_image_internal (char *file, int flip, int *color_typep) { Png_Type *p; png_uint_32 width, height, rowbytes; png_struct *png; png_info *info; int bit_depth; int interlace_type; int color_type; unsigned int sizeof_type; SLindex_Type dims[2]; SLtype data_type; png_byte **image_pointers = NULL; png_byte *data = NULL; SLang_Array_Type *at; void (*fixup_array_fun) (SLang_Array_Type *); if (NULL == (p = open_png_file (file))) return NULL; png = p->png; if (setjmp (png_jmpbuf (png))) { free_png_type (p); if (data != NULL) SLfree ((char *) data); free_image_pointers (image_pointers); SLang_verror (SL_Read_Error, "Error encountered during I/O to %s", file); return NULL; } png_init_io (png, p->fp); png_set_sig_bytes (png, 8); info = p->info; png_read_info(png, info); width = png_get_image_width (png, info); height = png_get_image_height (png, info); interlace_type = png_get_interlace_type (png, info); bit_depth = png_get_bit_depth (png, info); if (bit_depth == 16) png_set_strip_16 (png); switch (png_get_color_type (png, info)) { case PNG_COLOR_TYPE_GRAY: #if defined(PNG_LIBPNG_VER) && (PNG_LIBPNG_VER >= 10209) if (bit_depth < 8) png_set_expand_gray_1_2_4_to_8 (png); #else /* deprecated */ if (bit_depth < 8) png_set_gray_1_2_4_to_8 (png); #endif break; case PNG_COLOR_TYPE_GRAY_ALPHA: /* png_set_gray_to_rgb (png); */ break; case PNG_COLOR_TYPE_PALETTE: png_set_palette_to_rgb (png); break; } if (png_get_valid(png, info, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png); png_read_update_info (png, info); color_type = png_get_color_type (png, info); switch (color_type) { case PNG_COLOR_TYPE_RGBA: sizeof_type = 4; fixup_array_fun = fixup_array_rgba; data_type = SLang_get_int_type (32); break; case PNG_COLOR_TYPE_RGB: sizeof_type = 4; fixup_array_fun = fixup_array_rgb; data_type = SLang_get_int_type (32); break; case PNG_COLOR_TYPE_GRAY_ALPHA: sizeof_type = 2; fixup_array_fun = fixup_array_ga; data_type = SLang_get_int_type (16); break; case PNG_COLOR_TYPE_GRAY: sizeof_type = 1; fixup_array_fun = NULL; data_type = SLANG_UCHAR_TYPE; break; default: SLang_verror (SL_Read_Error, "Unsupported PNG color-type"); free_png_type (p); return NULL; } *color_typep = color_type; /* Use the high-level interface */ rowbytes = png_get_rowbytes (png, info); if (rowbytes > width * sizeof_type) { SLang_verror (SL_INTERNAL_ERROR, "Unexpected value returned from png_get_rowbytes"); free_png_type (p); return NULL; } if (NULL == (data = (png_byte *) SLmalloc (height * width * sizeof_type))) { free_png_type (p); return NULL; } if (NULL == (image_pointers = allocate_image_pointers (height, data, width * sizeof_type, flip))) { SLfree ((char *) data); free_png_type (p); return NULL; } png_read_image(png, image_pointers); dims[0] = height; dims[1] = width; if (NULL == (at = SLang_create_array (data_type, 0, (VOID_STAR) data, dims, 2))) { SLfree ((char *) data); free_image_pointers (image_pointers); free_png_type (p); return NULL; } free_png_type (p); free_image_pointers (image_pointers); if (fixup_array_fun != NULL) (*fixup_array_fun) (at); return at; }
s32 pngDecOpen(PPUThread& ppu, PHandle handle, PPStream png_stream, PSrc source, POpenInfo open_info, PCbControlStream control_stream = vm::null, POpenParam open_param = vm::null) { // Check if partial image decoding is used if (control_stream || open_param) { throw EXCEPTION("Partial image decoding is not supported."); } // Allocate memory for the stream structure auto stream = vm::ptr<PngStream>::make(handle->malloc(ppu, sizeof(PngStream), handle->malloc_arg).addr()); // Check if the allocation of memory for the stream structure failed if (!stream) { cellPngDec.error("PNG stream creation failed."); return CELL_PNGDEC_ERROR_FATAL; } // Set memory info open_info->initSpaceAllocated = sizeof(PngStream); // Set the stream source to the source give by the game stream->source = *source; // Indicate that a fixed alpha value isn't used, if not specified otherwise stream->fixed_alpha = false; // Use virtual memory address as a handle *png_stream = stream; // Allocate memory for the PNG buffer for decoding auto buffer = vm::ptr<PngBuffer>::make(handle->malloc(ppu, sizeof(PngBuffer), handle->malloc_arg).addr()); // Check for if the buffer structure allocation failed if (!buffer) { throw EXCEPTION("Memory allocation for the PNG buffer structure failed."); } // We might not be reading from a file stream buffer->file = false; // Set the buffer pointer in the stream structure, so we can later deallocate it stream->buffer = buffer; // Open the buffer/file and check the header u8 header[8]; // Need to test it somewhere if (stream->source.fileOffset != 0) { throw EXCEPTION("Non-0 file offset not supported."); } // Depending on the source type, get the first 8 bytes if (source->srcSelect == CELL_PNGDEC_FILE) { // Open a file stream fs::file file_stream(vfs::get(stream->source.fileName.get_ptr())); // Check if opening of the PNG file failed if (!file_stream) { cellPngDec.error("Opening of PNG failed. (%s)", stream->source.fileName.get_ptr()); return CELL_PNGDEC_ERROR_OPEN_FILE; } // Read the header if (file_stream.read(header, 8) != 8) { cellPngDec.error("PNG header is too small."); return CELL_PNGDEC_ERROR_HEADER; } // Get the file descriptor buffer->fd = idm::make<lv2_file_t>(std::move(file_stream), 0, 0); // Indicate that we need to read from a file stream buffer->file = true; } else { // We can simply copy the first 8 bytes memcpy(header, stream->source.streamPtr.get_ptr(), 8); } // Check if the header indicates a valid PNG file if (png_sig_cmp(header, 0, 8)) { cellPngDec.error("PNG signature is invalid."); return CELL_PNGDEC_ERROR_HEADER; } // Create a libpng structure, also pass our custom error/warning functions stream->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, pngDecError, pngDecWarning); // Check if the creation of the structure failed if (!stream->png_ptr) { cellPngDec.error("Creation of png_structp failed."); return CELL_PNGDEC_ERROR_FATAL; } // Create a libpng info structure stream->info_ptr = png_create_info_struct(stream->png_ptr); // Check if the creation of the structure failed if (!stream->info_ptr) { throw EXCEPTION("Creation of png_infop failed."); } // Set a point to return to when an error occurs in libpng if (setjmp(png_jmpbuf(stream->png_ptr))) { throw EXCEPTION("Fatal error in libpng."); } // We must indicate, that we allocated more memory open_info->initSpaceAllocated += sizeof(PngBuffer); // Init the IO for either reading from a file or a buffer if (source->srcSelect == CELL_PNGDEC_BUFFER) { // Set the data pointer and the file size buffer->length = stream->source.fileSize; buffer->data = stream->source.streamPtr; // Since we already read the header, we start reading from position 8 buffer->cursor = 8; } // Set the custom read function for decoding png_set_read_fn(stream->png_ptr, buffer.get_ptr(), pngDecReadBuffer); // We need to tell libpng, that we already read 8 bytes png_set_sig_bytes(stream->png_ptr, 8); // Read the basic info of the PNG file png_read_info(stream->png_ptr, stream->info_ptr); // Read the header info for future use stream->info.imageWidth = png_get_image_width(stream->png_ptr, stream->info_ptr); stream->info.imageHeight = png_get_image_height(stream->png_ptr, stream->info_ptr); stream->info.numComponents = png_get_channels(stream->png_ptr, stream->info_ptr); stream->info.colorSpace = getPngDecColourType(png_get_color_type(stream->png_ptr, stream->info_ptr)); stream->info.bitDepth = png_get_bit_depth(stream->png_ptr, stream->info_ptr); stream->info.interlaceMethod = png_get_interlace_type(stream->png_ptr, stream->info_ptr); stream->info.chunkInformation = pngDecGetChunkInformation(stream); return CELL_OK; }
static int read_png(FILE *fp) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); png_infop info_ptr = NULL; png_bytep row = NULL, display = NULL; if (png_ptr == NULL) return 0; if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if (row != NULL) free(row); if (display != NULL) free(display); return 0; } png_init_io(png_ptr, fp); info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) png_error(png_ptr, "OOM allocating info structure"); png_set_keep_unknown_chunks(png_ptr, PNG_HANDLE_CHUNK_ALWAYS, NULL, 0); png_read_info(png_ptr, info_ptr); { png_size_t rowbytes = png_get_rowbytes(png_ptr, info_ptr); /* Failure to initialize these is harmless */ row = malloc(rowbytes); display = malloc(rowbytes); if (row == NULL || display == NULL) png_error(png_ptr, "OOM allocating row buffers"); { png_uint_32 height = png_get_image_height(png_ptr, info_ptr); # ifdef PNG_READ_INTERLACING_SUPPORTED int passes = png_set_interlace_handling(png_ptr); # else /* !READ_INTERLACING */ int passes = png_get_interlace_type(png_ptr, info_ptr) == PNG_INTERLACE_ADAM7 ? PNG_INTERLACE_ADAM7_PASSES : 1; # endif /* !READ_INTERLACING */ int pass; png_start_read_image(png_ptr); for (pass = 0; pass < passes; ++pass) { png_uint_32 y = height; # ifndef PNG_READ_INTERLACING_SUPPORTED if (passes == PNG_INTERLACE_ADAM7_PASSES) y = PNG_PASS_ROWS(y, pass); # endif /* READ_INTERLACING */ /* NOTE: this trashes the row each time; interlace handling won't * work, but this avoids memory thrashing for speed testing. */ while (y-- > 0) png_read_row(png_ptr, row, display); } } } /* Make sure to read to the end of the file: */ png_read_end(png_ptr, info_ptr); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); free(row); free(display); return 1; }
int main(int argc, char **argv) { static char *usage="Usage:\n\t%s png__file\n"; int i; FILE *fp_in = NULL; png_structp png_p; png_infop info_p; char header[8]; int bit_depth; int color_type; png_color_16p input_backgrd; double gammaval; int file_width, file_height; png_int_32 xoff, yoff; png_uint_32 xres, yres; int unit_type; int rgb_intent; double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; png_timep mod_time; png_textp text; int num_text; unsigned char *image; unsigned char **rows; if (argc != 2) { bu_log(usage, argv[0]); bu_exit(EXIT_FAILURE, "Incorrect number of arguments!!\n"); } else { if ((fp_in = fopen(argv[1], "rb")) == NULL) { perror(argv[1]); bu_log("png_onfo: cannot open \"%s\" for reading\n", argv[1]); bu_exit(EXIT_FAILURE, "Cannot open input file\n"); } } if (fread(header, 8, 1, fp_in) != 1) bu_exit(EXIT_FAILURE, "ERROR: Failed while reading file header!!!\n"); if (png_sig_cmp((png_bytep)header, 0, 8)) bu_exit(EXIT_FAILURE, "This is not a PNG file!!!\n"); png_p = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_p) bu_exit(EXIT_FAILURE, "png_create_read_struct() failed!!\n"); info_p = png_create_info_struct(png_p); if (!info_p) bu_exit(EXIT_FAILURE, "png_create_info_struct() failed!!\n"); png_init_io(png_p, fp_in); png_set_sig_bytes(png_p, 8); png_read_info(png_p, info_p); color_type = png_get_color_type(png_p, info_p); bit_depth = png_get_bit_depth(png_p, info_p); switch (color_type) { case PNG_COLOR_TYPE_GRAY: bu_log("color type: b/w (bit depth=%d)\n", bit_depth); break; case PNG_COLOR_TYPE_GRAY_ALPHA: bu_log("color type: b/w with alpha channel (bit depth=%d)\n", bit_depth); break; case PNG_COLOR_TYPE_PALETTE: bu_log("color type: color palette (bit depth=%d)\n", bit_depth); break; case PNG_COLOR_TYPE_RGB: bu_log("color type: RGB (bit depth=%d)\n", bit_depth); break; case PNG_COLOR_TYPE_RGB_ALPHA: bu_log("color type: RGB with alpha channel (bit depth=%d)\n", bit_depth); break; default: bu_log("Unrecognized color type (bit depth=%d)\n", bit_depth); break; } file_width = png_get_image_width(png_p, info_p); file_height = png_get_image_height(png_p, info_p); bu_log("Image size: %d X %d\n", file_width, file_height); /* allocate memory for image */ image = (unsigned char *)bu_calloc(1, file_width*file_height*3, "image"); /* create rows array */ rows = (unsigned char **)bu_calloc(file_height, sizeof(unsigned char *), "rows"); for (i=0; i<file_height; i++) rows[file_height-1-i] = image+(i*file_width*3); png_read_image(png_p, rows); if (png_get_oFFs(png_p, info_p, &xoff, &yoff, &unit_type)) { if (unit_type == PNG_OFFSET_PIXEL) bu_log("X Offset: %d pixels\nY Offset: %d pixels\n", (int)xoff, (int)yoff); else if (unit_type == PNG_OFFSET_MICROMETER) bu_log("X Offset: %d um\nY Offset: %d um\n", (int)xoff, (int)yoff); } if (png_get_pHYs(png_p, info_p, &xres, &yres, &unit_type)) { if (unit_type == PNG_RESOLUTION_UNKNOWN) bu_log("Aspect ratio: %g (width/height)\n", (double)xres/(double)yres); else if (unit_type == PNG_RESOLUTION_METER) bu_log("pixel density:\n\t%d pixels/m horizontal\n\t%d pixels/m vertical\n", (int)xres, (int)yres); } if (png_get_interlace_type(png_p, info_p) == PNG_INTERLACE_NONE) bu_log("not interlaced\n"); else bu_log("interlaced\n"); if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA || color_type == PNG_COLOR_TYPE_RGB_ALPHA) if (png_get_bKGD(png_p, info_p, &input_backgrd)) bu_log("background color: %d %d %d\n", input_backgrd->red, input_backgrd->green, input_backgrd->blue); if (png_get_sRGB(png_p, info_p, &rgb_intent)) { bu_log("rendering intent: "); switch (rgb_intent) { case PNG_sRGB_INTENT_SATURATION: bu_log("saturation\n"); break; case PNG_sRGB_INTENT_PERCEPTUAL: bu_log("perceptual\n"); break; case PNG_sRGB_INTENT_ABSOLUTE: bu_log("absolute\n"); break; case PNG_sRGB_INTENT_RELATIVE: bu_log("relative\n"); break; } } if (png_get_gAMA(png_p, info_p, &gammaval)) bu_log("gamma: %g\n", gammaval); #if defined(PNG_READ_cHRM_SUPPORTED) if (png_get_cHRM(png_p, info_p, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y)) { bu_log("Chromaticity:\n"); bu_log("\twhite point\t(%g %g)\n\tred\t(%g %g)\n\tgreen\t(%g %g)\n\tblue\t(%g %g)\n", white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y); } #endif if (png_get_text(png_p, info_p, &text, &num_text)) for (i=0; i<num_text; i++) bu_log("%s: %s\n", text[i].key, text[i].text); if (png_get_tIME(png_p, info_p, &mod_time)) bu_log("Last modified: %d/%d/%d %d:%d:%d\n", mod_time->month, mod_time->day, mod_time->year, mod_time->hour, mod_time->minute, mod_time->second); return 0; }
PyObject * load_png_fast_progressive (char *filename, PyObject *get_buffer_callback) { // Note: we are not using the method that libpng calls "Reading PNG // files progressively". That method would involve feeding the data // into libpng piece by piece, which is not necessary if we can give // libpng a simple FILE pointer. png_structp png_ptr = NULL; png_infop info_ptr = NULL; PyObject * result = NULL; FILE *fp = NULL; uint32_t width, height; uint32_t rows_left; png_byte color_type; png_byte bit_depth; bool have_alpha; char *cm_processing = NULL; // ICC profile-based colour conversion data. png_charp icc_profile_name = NULL; int icc_compression_type = 0; #if PNG_LIBPNG_VER < 10500 // 1.5.0beta36, according to libpng CHANGES png_charp icc_profile = NULL; #else png_bytep icc_profile = NULL; #endif png_uint_32 icc_proflen = 0; // The sRGB flag has an intent field, which we ignore - // the target gamut is sRGB already. int srgb_intent = 0; // Generic RGB space conversion params. // The assumptions we're making are those of sRGB, // but they'll be overridden by gammas or primaries in the file if used. bool generic_rgb_have_gAMA = false; bool generic_rgb_have_cHRM = false; double generic_rgb_file_gamma = 45455 / PNG_gAMA_scale; double generic_rgb_white_x = 31270 / PNG_cHRM_scale; double generic_rgb_white_y = 32900 / PNG_cHRM_scale; double generic_rgb_red_x = 64000 / PNG_cHRM_scale; double generic_rgb_red_y = 33000 / PNG_cHRM_scale; double generic_rgb_green_x = 30000 / PNG_cHRM_scale; double generic_rgb_green_y = 60000 / PNG_cHRM_scale; double generic_rgb_blue_x = 15000 / PNG_cHRM_scale; double generic_rgb_blue_y = 6000 / PNG_cHRM_scale; // Indicates the case where no CM information was present in the file and we // treated it as sRGB. bool possible_legacy_png = false; // LCMS stuff cmsHPROFILE input_buffer_profile = NULL; cmsHPROFILE nparray_data_profile = cmsCreate_sRGBProfile(); cmsHTRANSFORM input_buffer_to_nparray = NULL; cmsToneCurve *gamma_transfer_func = NULL; cmsUInt32Number input_buffer_format = 0; cmsSetLogErrorHandler(log_lcms2_error); fp = fopen(filename, "rb"); if (!fp) { PyErr_SetFromErrno(PyExc_IOError); //PyErr_Format(PyExc_IOError, "Could not open PNG file for writing: %s", // filename); goto cleanup; } png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, (png_voidp)NULL, png_read_error_callback, NULL); if (!png_ptr) { PyErr_SetString(PyExc_MemoryError, "png_create_write_struct() failed"); goto cleanup; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { PyErr_SetString(PyExc_MemoryError, "png_create_info_struct() failed"); goto cleanup; } if (setjmp(png_jmpbuf(png_ptr))) { goto cleanup; } png_init_io(png_ptr, fp); png_read_info(png_ptr, info_ptr); // If there's an embedded ICC profile, use it in preference to any other // colour management information present. if (png_get_iCCP (png_ptr, info_ptr, &icc_profile_name, &icc_compression_type, &icc_profile, &icc_proflen)) { input_buffer_profile = cmsOpenProfileFromMem(icc_profile, icc_proflen); if (! input_buffer_profile) { PyErr_SetString(PyExc_MemoryError, "cmsOpenProfileFromMem() failed"); goto cleanup; } cm_processing = "iCCP (use embedded colour profile)"; } // Shorthand for sRGB. else if (png_get_sRGB (png_ptr, info_ptr, &srgb_intent)) { input_buffer_profile = cmsCreate_sRGBProfile(); cm_processing = "sRGB (explicit sRGB chunk)"; } else { // We might have generic RGB transformation information in the form of // the chromaticities for R, G and B and a generic gamma curve. if (png_get_cHRM (png_ptr, info_ptr, &generic_rgb_white_x, &generic_rgb_white_y, &generic_rgb_red_x, &generic_rgb_red_y, &generic_rgb_green_x, &generic_rgb_green_y, &generic_rgb_blue_x, &generic_rgb_blue_y)) { generic_rgb_have_cHRM = true; } if (png_get_gAMA(png_ptr, info_ptr, &generic_rgb_file_gamma)) { generic_rgb_have_gAMA = true; } if (generic_rgb_have_gAMA || generic_rgb_have_cHRM) { cmsCIExyYTRIPLE primaries = {{generic_rgb_red_x, generic_rgb_red_y}, {generic_rgb_green_x, generic_rgb_green_y}, {generic_rgb_blue_x, generic_rgb_blue_y}}; cmsCIExyY white_point = {generic_rgb_white_x, generic_rgb_white_y}; gamma_transfer_func = cmsBuildGamma(NULL, 1.0/generic_rgb_file_gamma); cmsToneCurve *transfer_funcs[3] = {gamma_transfer_func, gamma_transfer_func, gamma_transfer_func }; input_buffer_profile = cmsCreateRGBProfile(&white_point, &primaries, transfer_funcs); cm_processing = "cHRM and/or gAMA (generic RGB space)"; } // Possible legacy PNG, or rather one which might have been written with an // old version of MyPaint. Treat as sRGB, but flag the strangeness because // it might be important for PNGs in old OpenRaster files. else { possible_legacy_png = true; input_buffer_profile = cmsCreate_sRGBProfile(); cm_processing = "sRGB (no CM chunks present)"; } } if (png_get_interlace_type (png_ptr, info_ptr) != PNG_INTERLACE_NONE) { PyErr_SetString(PyExc_RuntimeError, "Interlaced PNG files are not supported!"); goto cleanup; } color_type = png_get_color_type(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); have_alpha = color_type & PNG_COLOR_MASK_ALPHA; if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) { png_set_expand_gray_1_2_4_to_8(png_ptr); } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); have_alpha = true; } if (bit_depth < 8) { png_set_packing(png_ptr); } if (!have_alpha) { png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); } if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { png_set_gray_to_rgb(png_ptr); } png_read_update_info(png_ptr, info_ptr); // Verify what we have done bit_depth = png_get_bit_depth(png_ptr, info_ptr); if (! (bit_depth == 8 || bit_depth == 16)) { PyErr_SetString(PyExc_RuntimeError, "Failed to convince libpng to convert " "to 8 or 16 bits per channel"); goto cleanup; } if (png_get_color_type(png_ptr, info_ptr) != PNG_COLOR_TYPE_RGB_ALPHA) { PyErr_SetString(PyExc_RuntimeError, "Failed to convince libpng to convert " "to RGBA (wrong color_type)"); goto cleanup; } if (png_get_channels(png_ptr, info_ptr) != 4) { PyErr_SetString(PyExc_RuntimeError, "Failed to convince libpng to convert " "to RGBA (wrong number of channels)"); goto cleanup; } // PNGs use network byte order, i.e. big-endian in descending order // of bit significance. LittleCMS uses whatever's detected for the compiler. // ref: http://www.w3.org/TR/2003/REC-PNG-20031110/#7Integers-and-byte-order if (bit_depth == 16) { #ifdef CMS_USE_BIG_ENDIAN input_buffer_format = TYPE_RGBA_16; #else input_buffer_format = TYPE_RGBA_16_SE; #endif } else { input_buffer_format = TYPE_RGBA_8; } input_buffer_to_nparray = cmsCreateTransform (input_buffer_profile, input_buffer_format, nparray_data_profile, TYPE_RGBA_8, INTENT_PERCEPTUAL, 0); width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); rows_left = height; while (rows_left) { PyObject *pyarr = NULL; uint32_t rows = 0; uint32_t row = 0; const uint8_t input_buf_bytes_per_pixel = (bit_depth==8) ? 4 : 8; const uint32_t input_buf_row_stride = sizeof(png_byte) * width * input_buf_bytes_per_pixel; png_byte *input_buffer = NULL; png_bytep *input_buf_row_pointers = NULL; pyarr = PyObject_CallFunction(get_buffer_callback, "ii", width, height); if (! pyarr) { PyErr_Format(PyExc_RuntimeError, "Get-buffer callback failed"); goto cleanup; } #ifdef HEAVY_DEBUG //assert(PyArray_ISCARRAY(arr)); assert(PyArray_NDIM(pyarr) == 3); assert(PyArray_DIM(pyarr, 1) == width); assert(PyArray_DIM(pyarr, 2) == 4); assert(PyArray_TYPE(pyarr) == NPY_UINT8); assert(PyArray_ISBEHAVED(pyarr)); assert(PyArray_STRIDE(pyarr, 1) == 4*sizeof(uint8_t)); assert(PyArray_STRIDE(pyarr, 2) == sizeof(uint8_t)); #endif rows = PyArray_DIM(pyarr, 0); if (rows > rows_left) { PyErr_Format(PyExc_RuntimeError, "Attempt to read %d rows from the PNG, " "but only %d are left", rows, rows_left); goto cleanup; } input_buffer = (png_byte *) malloc(rows * input_buf_row_stride); input_buf_row_pointers = (png_bytep *)malloc(rows * sizeof(png_bytep)); for (row=0; row<rows; row++) { input_buf_row_pointers[row] = input_buffer + (row * input_buf_row_stride); } png_read_rows(png_ptr, input_buf_row_pointers, NULL, rows); rows_left -= rows; for (row=0; row<rows; row++) { uint8_t *pyarr_row = (uint8_t *)PyArray_DATA(pyarr) + row*PyArray_STRIDE(pyarr, 0); uint8_t *input_row = input_buf_row_pointers[row]; // Really minimal fake colour management. Just remaps to sRGB. cmsDoTransform(input_buffer_to_nparray, input_row, pyarr_row, width); // lcms2 ignores alpha, so copy that verbatim // If it's 8bpc RGBA, use A. // If it's 16bpc RrGgBbAa, use A. for (uint32_t i=0; i<width; ++i) { const uint32_t pyarr_alpha_byte = (i*4) + 3; const uint32_t buf_alpha_byte = (i*input_buf_bytes_per_pixel) + ((bit_depth==8) ? 3 : 6); pyarr_row[pyarr_alpha_byte] = input_row[buf_alpha_byte]; } } free(input_buf_row_pointers); free(input_buffer); Py_DECREF(pyarr); } png_read_end(png_ptr, NULL); result = Py_BuildValue("{s:b,s:i,s:i,s:s}", "possible_legacy_png", possible_legacy_png, "width", width, "height", height, "cm_conversions_applied", cm_processing); cleanup: if (info_ptr) png_destroy_read_struct (&png_ptr, &info_ptr, NULL); // libpng's style is to free internally allocated stuff like the icc // tables in png_destroy_*(). I think. if (fp) fclose(fp); if (input_buffer_profile) cmsCloseProfile(input_buffer_profile); if (nparray_data_profile) cmsCloseProfile(nparray_data_profile); if (input_buffer_to_nparray) cmsDeleteTransform(input_buffer_to_nparray); if (gamma_transfer_func) cmsFreeToneCurve(gamma_transfer_func); return result; }
unsigned char * ReadPNG(FILE *infile,int *width, int *height, XColor *colrs) { unsigned char *pixmap; unsigned char *p; png_byte *q; png_struct *png_ptr; png_info *info_ptr; double screen_gamma; png_byte *png_pixels=NULL, **row_pointers=NULL; int i, j; unsigned int packets; png_color std_color_cube[216]; /* first check to see if its a valid PNG file. If not, return. */ /* we assume that infile is a valid filepointer */ { int ret; png_byte buf[8]; ret = fread(buf, 1, 8, infile); if(ret != 8) return 0; ret = png_check_sig(buf, 8); if(!ret) return(0); } /* OK, it is a valid PNG file, so let's rewind it, and start decoding it */ rewind(infile); /* allocate the structures */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if(!png_ptr) return 0; info_ptr = png_create_info_struct(png_ptr); if(!info_ptr) { png_destroy_read_struct(&png_ptr, NULL, NULL); return 0; } /* Establish the setjmp return context for png_error to use. */ if (setjmp(png_jmpbuf(png_ptr))) { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr, "\n!!!libpng read error!!!\n"); } #endif png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if(png_pixels != NULL) free((char *)png_pixels); if(row_pointers != NULL) free((png_byte **)row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return 0; } /* set up the input control */ png_init_io(png_ptr, infile); /* read the file information */ png_read_info(png_ptr, info_ptr); /* setup other stuff using the fields of png_info. */ *width = (int)png_get_image_width(png_ptr, info_ptr); *height = (int)png_get_image_height(png_ptr, info_ptr); #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"\n\nBEFORE\nheight = %d\n", (int)png_get_image_width(png_ptr, info_ptr)); fprintf(stderr,"width = %d\n", (int)png_get_image_height(png_ptr, info_ptr)); fprintf(stderr,"bit depth = %d\n", png_get_bit_depth(png_ptr, info_ptr)); fprintf(stderr,"color type = %d\n", png_get_color_type(png_ptr, info_ptr)); fprintf(stderr,"compression type = %d\n", png_get_compression_type(png_ptr, info_ptr)); fprintf(stderr,"filter type = %d\n", png_get_filter_type(png_ptr, info_ptr)); fprintf(stderr,"interlace type = %d\n", png_get_interlace_type(png_ptr, info_ptr)); fprintf(stderr,"num colors = %d\n", png_get_palette_max(png_ptr, info_ptr)); fprintf(stderr,"rowbytes = %d\n", png_get_rowbytes(png_ptr, info_ptr)); } #endif #if 0 /* This handles alpha and transparency by replacing it with a background value. */ /* its #if'ed out for now cause I don't have anything to test it with */ { png_color_16 my_background; if (png_get_valid(png_ptr, info_ptr) & PNG_INFO_bKGD) png_set_background(png_ptr, &(info_ptr->background), PNG_GAMMA_FILE, 1, 1.0); else png_set_background(png_ptr, &my_background, PNG_GAMMA_SCREEN, 0, 1.0); } #endif /* strip pixels in 16-bit images down to 8 bits */ if (png_get_bit_depth(png_ptr, info_ptr) == 16) png_set_strip_16(png_ptr); /* If it is a color image then check if it has a palette. If not then dither the image to 256 colors, and make up a palette */ if (png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB || png_get_color_type(png_ptr, info_ptr)==PNG_COLOR_TYPE_RGB_ALPHA) { if(! (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) ) { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"dithering (RGB->palette)...\n"); } #endif /* if there is is no valid palette, then we need to make one up */ for(i=0;i<216;i++) { /* 255.0/5 = 51 */ std_color_cube[i].red=(i%6)*51; std_color_cube[i].green=((i/6)%6)*51; std_color_cube[i].blue=(i/36)*51; } /* this should probably be dithering to Rdata.colors_per_inlined_image colors */ png_set_quantize(png_ptr, std_color_cube, 216, 216, NULL, 1); } else { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"dithering (RGB->file supplied palette)...\n"); } #endif png_colorp palette; int num_palette; png_get_PLTE(png_ptr, info_ptr, palette, &num_palette); png_uint_16p hist; png_get_hIST(png_ptr, info_ptr, &hist); png_set_quantize(png_ptr, palette, num_palette, get_pref_int(eCOLORS_PER_INLINED_IMAGE), hist, 1); } } /* PNG files pack pixels of bit depths 1, 2, and 4 into bytes as small as they can. This expands pixels to 1 pixel per byte, and if a transparency value is supplied, an alpha channel is built.*/ if (png_get_bit_depth(png_ptr, info_ptr) < 8) png_set_packing(png_ptr); /* have libpng handle the gamma conversion */ if (get_pref_boolean(eUSE_SCREEN_GAMMA)) { /*SWP*/ if (png_get_bit_depth(png_ptr, info_ptr) != 16) { /* temporary .. glennrp */ screen_gamma=(double)(get_pref_float(eSCREEN_GAMMA)); #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"screen gamma=%f\n",screen_gamma); } #endif if (png_get_valid(png_ptr, info_ptr, PNG_INFO_gAMA)) { double gamma; png_get_gAMA(png_ptr, info_ptr, &gamma); #ifndef DISABLE_TRACE if (srcTrace) { printf("setting gamma=%f\n",gamma); } #endif png_set_gamma(png_ptr, screen_gamma, gamma); } else { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"setting gamma=%f\n",0.45); } #endif png_set_gamma(png_ptr, screen_gamma, (double)0.45); } } } if (png_get_interlace_type(png_ptr, info_ptr)) png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"\n\nAFTER\nheight = %d\n", (int)png_get_image_width(png_ptr, info_ptr)); fprintf(stderr,"width = %d\n", (int)png_get_image_height(png_ptr, info_ptr)); fprintf(stderr,"bit depth = %d\n", png_get_bit_depth(png_ptr, info_ptr)); fprintf(stderr,"color type = %d\n", png_get_color_type(png_ptr, info_ptr)); fprintf(stderr,"compression type = %d\n", png_get_compression_type(png_ptr, info_ptr)); fprintf(stderr,"filter type = %d\n", png_get_filter_type(png_ptr, info_ptr)); fprintf(stderr,"interlace type = %d\n", png_get_interlace_type(png_ptr, info_ptr)); fprintf(stderr,"num colors = %d\n", png_get_palette_max(png_ptr, info_ptr)); fprintf(stderr,"rowbytes = %d\n", png_get_rowbytes(png_ptr, info_ptr)); } #endif /* allocate the pixel grid which we will need to send to png_read_image(). */ png_pixels = (png_byte *)malloc(png_get_rowbytes(png_ptr, info_ptr) * (*height) * sizeof(png_byte)); row_pointers = (png_byte **) malloc((*height) * sizeof(png_byte *)); for (i=0; i < *height; i++) row_pointers[i]=png_pixels+(png_get_rowbytes(png_ptr, info_ptr)*i); /* FINALLY - read the darn thing. */ png_read_image(png_ptr, row_pointers); /* now that we have the (transformed to 8-bit RGB) image, we have to copy the resulting palette to our colormap. */ if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_COLOR) { if (png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) { png_colorp palette; int num_palette = png_get_palette_max(png_ptr, info_ptr); for (i=0; i < num_palette; i++) { png_get_PLTE(png_ptr, info_ptr, &palette, i); colrs[i].red = palette->red << 8; colrs[i].green = palette->green << 8; colrs[i].blue = palette->blue << 8; colrs[i].pixel = i; colrs[i].flags = DoRed|DoGreen|DoBlue; } } else { for (i=0; i < 216; i++) { colrs[i].red = std_color_cube[i].red << 8; colrs[i].green = std_color_cube[i].green << 8; colrs[i].blue = std_color_cube[i].blue << 8; colrs[i].pixel = i; colrs[i].flags = DoRed|DoGreen|DoBlue; } } } else { /* grayscale image */ for(i=0; i < 256; i++ ) { colrs[i].red = i << 8; colrs[i].green = i << 8; colrs[i].blue = i << 8; colrs[i].pixel = i; colrs[i].flags = DoRed|DoGreen|DoBlue; } } /* Now copy the pixel data from png_pixels to pixmap */ pixmap = (png_byte *)malloc((*width) * (*height) * sizeof(png_byte)); p = pixmap; q = png_pixels; /* if there is an alpha channel, we have to get rid of it in the pixmap, since I don't do anything with it yet */ if (png_get_color_type(png_ptr, info_ptr) & PNG_COLOR_MASK_ALPHA) { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"Getting rid of alpha channel\n"); } #endif for(i=0; i<*height; i++) { q = row_pointers[i]; for(j=0; j<*width; j++) { *p++ = *q++; /*palette index*/ q++; /* skip the alpha pixel */ } } free((char *)png_pixels); } else { #ifndef DISABLE_TRACE if (srcTrace) { fprintf(stderr,"No alpha channel\n"); } #endif for(i=0; i<*height; i++) { q = row_pointers[i]; for(j=0; j<*width; j++) { *p++ = *q++; /*palette index*/ } } free((char *)png_pixels); } free((png_byte **)row_pointers); /* clean up after the read, and free any memory allocated */ /* free the structure */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return pixmap; }
static HPDF_STATUS LoadPngData (HPDF_Dict image, HPDF_Xref xref, HPDF_Stream png_data, HPDF_BOOL delayed_loading) { HPDF_STATUS ret = HPDF_OK; png_uint_32 width, height; int bit_depth, color_type; png_structp png_ptr = NULL; png_infop info_ptr = NULL; HPDF_PTRACE ((" HPDF_Image_LoadPngImage\n")); /* create read_struct. */ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, image->error, PngErrorFunc, PngErrorFunc); if (png_ptr == NULL) { HPDF_SetError (image->error, HPDF_FAILD_TO_ALLOC_MEM, 0); return HPDF_FAILD_TO_ALLOC_MEM; } /* create info-struct */ info_ptr = png_create_info_struct (png_ptr); if (info_ptr == NULL) { HPDF_SetError (image->error, HPDF_FAILD_TO_ALLOC_MEM, 0); goto Exit; } png_set_sig_bytes (png_ptr, HPDF_PNG_BYTES_TO_CHECK); png_set_read_fn (png_ptr, (void *)png_data, (png_rw_ptr)&PngReadFunc); /* reading info structure. */ png_read_info(png_ptr, info_ptr); if (image->error->error_no != HPDF_OK) { goto Exit; } png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); /* 16bit images are not supported. */ if (bit_depth == 16) { png_set_strip_16(png_ptr); } png_read_update_info(png_ptr, info_ptr); if (image->error->error_no != HPDF_OK) { goto Exit; } /* check palette-based images for transparent areas and load them immediately if found */ if (xref && PNG_COLOR_TYPE_PALETTE & color_type) { png_bytep trans; int num_trans; HPDF_Dict smask; png_bytep smask_data; if (!png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS) || !png_get_tRNS(png_ptr, info_ptr, &trans, &num_trans, NULL)) { goto no_transparent_color_in_palette; } smask = HPDF_DictStream_New (image->mmgr, xref); if (!smask) { ret = HPDF_FAILD_TO_ALLOC_MEM; goto Exit; } smask->header.obj_class |= HPDF_OSUBCLASS_XOBJECT; ret = HPDF_Dict_AddName (smask, "Type", "XObject"); ret += HPDF_Dict_AddName (smask, "Subtype", "Image"); ret += HPDF_Dict_AddNumber (smask, "Width", (HPDF_UINT)width); ret += HPDF_Dict_AddNumber (smask, "Height", (HPDF_UINT)height); ret += HPDF_Dict_AddName (smask, "ColorSpace", "DeviceGray"); ret += HPDF_Dict_AddNumber (smask, "BitsPerComponent", (HPDF_UINT)bit_depth); if (ret != HPDF_OK) { HPDF_Dict_Free(smask); ret = HPDF_INVALID_PNG_IMAGE; goto Exit; } smask_data = HPDF_GetMem(image->mmgr, width * height); if (!smask_data) { HPDF_Dict_Free(smask); ret = HPDF_FAILD_TO_ALLOC_MEM; goto Exit; } if (ReadTransparentPaletteData(image, png_ptr, info_ptr, smask_data, trans, num_trans) != HPDF_OK) { HPDF_FreeMem(image->mmgr, smask_data); HPDF_Dict_Free(smask); ret = HPDF_INVALID_PNG_IMAGE; goto Exit; } if (HPDF_Stream_Write(smask->stream, smask_data, width * height) != HPDF_OK) { HPDF_FreeMem(image->mmgr, smask_data); HPDF_Dict_Free(smask); ret = HPDF_FILE_IO_ERROR; goto Exit; } HPDF_FreeMem(image->mmgr, smask_data); ret += CreatePallet(image, png_ptr, info_ptr); ret += HPDF_Dict_AddNumber (image, "Width", (HPDF_UINT)width); ret += HPDF_Dict_AddNumber (image, "Height", (HPDF_UINT)height); ret += HPDF_Dict_AddNumber (image, "BitsPerComponent", (HPDF_UINT)bit_depth); ret += HPDF_Dict_Add (image, "SMask", smask); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return HPDF_OK; } no_transparent_color_in_palette: /* read images with alpha channel right away we have to do this because image transparent mask must be added to the Xref */ if (xref && PNG_COLOR_MASK_ALPHA & color_type) { HPDF_Dict smask; png_bytep smask_data; smask = HPDF_DictStream_New (image->mmgr, xref); if (!smask) { ret = HPDF_FAILD_TO_ALLOC_MEM; goto Exit; } smask->header.obj_class |= HPDF_OSUBCLASS_XOBJECT; ret = HPDF_Dict_AddName (smask, "Type", "XObject"); ret += HPDF_Dict_AddName (smask, "Subtype", "Image"); ret += HPDF_Dict_AddNumber (smask, "Width", (HPDF_UINT)width); ret += HPDF_Dict_AddNumber (smask, "Height", (HPDF_UINT)height); ret += HPDF_Dict_AddName (smask, "ColorSpace", "DeviceGray"); ret += HPDF_Dict_AddNumber (smask, "BitsPerComponent", (HPDF_UINT)bit_depth); if (ret != HPDF_OK) { HPDF_Dict_Free(smask); ret = HPDF_INVALID_PNG_IMAGE; goto Exit; } smask_data = HPDF_GetMem(image->mmgr, width * height); if (!smask_data) { HPDF_Dict_Free(smask); ret = HPDF_FAILD_TO_ALLOC_MEM; goto Exit; } if (ReadTransparentPngData(image, png_ptr, info_ptr, smask_data) != HPDF_OK) { HPDF_FreeMem(image->mmgr, smask_data); HPDF_Dict_Free(smask); ret = HPDF_INVALID_PNG_IMAGE; goto Exit; } if (HPDF_Stream_Write(smask->stream, smask_data, width * height) != HPDF_OK) { HPDF_FreeMem(image->mmgr, smask_data); HPDF_Dict_Free(smask); ret = HPDF_FILE_IO_ERROR; goto Exit; } HPDF_FreeMem(image->mmgr, smask_data); if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { ret += HPDF_Dict_AddName (image, "ColorSpace", "DeviceGray"); } else { ret += HPDF_Dict_AddName (image, "ColorSpace", "DeviceRGB"); } ret += HPDF_Dict_AddNumber (image, "Width", (HPDF_UINT)width); ret += HPDF_Dict_AddNumber (image, "Height", (HPDF_UINT)height); ret += HPDF_Dict_AddNumber (image, "BitsPerComponent", (HPDF_UINT)bit_depth); ret += HPDF_Dict_Add (image, "SMask", smask); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return HPDF_OK; } /* if the image has color palette, copy the pallet of the image to * create color map. */ if (color_type == PNG_COLOR_TYPE_PALETTE) ret = CreatePallet(image, png_ptr, info_ptr); else if (color_type == PNG_COLOR_TYPE_GRAY) ret = HPDF_Dict_AddName (image, "ColorSpace", "DeviceGray"); else ret = HPDF_Dict_AddName (image, "ColorSpace", "DeviceRGB"); if (ret != HPDF_OK) goto Exit; /* read image-data * if the image is interlaced, read whole image at once. * if delayed_loading is HPDF_TRUE, the data does not load this phase. */ if (delayed_loading) { image->before_write_fn = PngBeforeWrite; image->after_write_fn = PngAfterWrite; } else { if (png_get_interlace_type(png_ptr, info_ptr) != PNG_INTERLACE_NONE) ret = ReadPngData_Interlaced(image, png_ptr, info_ptr); else ret = ReadPngData(image, png_ptr, info_ptr); if (ret != HPDF_OK) goto Exit; } /* setting the info of the image. */ if (HPDF_Dict_AddNumber (image, "Width", (HPDF_UINT)width) != HPDF_OK) goto Exit; if (HPDF_Dict_AddNumber (image, "Height", (HPDF_UINT)height) != HPDF_OK) goto Exit; if (HPDF_Dict_AddNumber (image, "BitsPerComponent", (HPDF_UINT)bit_depth) != HPDF_OK) goto Exit; /* clean up */ png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return HPDF_OK; Exit: png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if (ret != HPDF_OK) { return ret; } return image->error->error_no; }
int PngDecoder::validate() { if (png_sig_cmp((png_bytep)buf->data(), 0, PNG_HEADER_SIZE)) { std::cout << "[PngDecoder] error: %s is not a PNG." << std::endl; return -1; } buf->drain(PNG_HEADER_SIZE); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) { std::cout << "[PngDecoder] error: png_create_read_struct returned 0." << std::endl; return -1; } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { std::cout << "[PngDecoder] error: png_create_info_struct returned 0." << std::endl; png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return -1; } end_info = png_create_info_struct(png_ptr); if (!end_info) { std::cout << "[PngDecoder] error: png_create_info_struct returned 0." << std::endl; png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); return -1; } if (setjmp(png_jmpbuf(png_ptr))) { std::cout << "[PngDecoder] error from libpng" << std::endl; png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return -1; } png_set_read_fn(png_ptr, buf, pngDecoderReadFunction); png_set_sig_bytes(png_ptr, PNG_HEADER_SIZE); png_read_info(png_ptr, info_ptr); png_set_expand(png_ptr); png_set_strip_16(png_ptr); png_set_gray_to_rgb(png_ptr); png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); png_set_interlace_handling(png_ptr); png_read_update_info(png_ptr, info_ptr); imgInfo.width = png_get_image_width(png_ptr, info_ptr); imgInfo.height = png_get_image_height(png_ptr, info_ptr); imgInfo.color_type = png_get_color_type(png_ptr, info_ptr); imgInfo.channels = png_get_channels(png_ptr, info_ptr); imgInfo.bit_depth = png_get_bit_depth(png_ptr, info_ptr); imgInfo.interlance_type = png_get_interlace_type(png_ptr, info_ptr); imgInfo.rowbytes = png_get_rowbytes(png_ptr, info_ptr); #ifdef _PNGDECODER_DEBUG std::cout << "[PngDecoder] Extracted w=" << imgInfo.width << " h=" << imgInfo.height << " color_type=" << imgInfo.color_type << " bit_depth="<< imgInfo.bit_depth << " rowbytes="<< imgInfo.rowbytes << " channels="<< imgInfo.channels << " interlance_type="<< imgInfo.interlance_type << std::endl; #endif if (imgInfo.bit_depth != 8) { std::cout << "[PngDecoder] error: Unsupported bit depth=" << imgInfo.bit_depth <<". Must be 8." << std::endl; return -1; } switch(imgInfo.color_type) { case PNG_COLOR_TYPE_RGB: imgInfo.format = GL_RGB; break; case PNG_COLOR_TYPE_RGB_ALPHA: imgInfo.format = GL_RGBA; break; case PNG_COLOR_TYPE_PALETTE: if (imgInfo.color_type & PNG_COLOR_MASK_ALPHA) { imgInfo.format = GL_RGBA; } else if (imgInfo.color_type & PNG_COLOR_MASK_COLOR) { imgInfo.format = GL_RGB; } break; default: std::cout << "[PngDecoder] error: unknown libpng color type=" << imgInfo.color_type << " rgb=" << PNG_COLOR_TYPE_RGB << " rgba=" << PNG_COLOR_TYPE_RGBA << " palette=" << PNG_COLOR_TYPE_PALETTE << std::endl; return -1; } return 0; }
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int) { if (m_frameBufferCache.isEmpty()) return; // Initialize the framebuffer if needed. ImageFrame& buffer = m_frameBufferCache[0]; if (buffer.status() == ImageFrame::FrameEmpty) { png_structp png = m_reader->pngPtr(); if (!buffer.setSize(size().width(), size().height())) { longjmp(JMPBUF(png), 1); return; } unsigned colorChannels = m_reader->hasAlpha() ? 4 : 3; if (PNG_INTERLACE_ADAM7 == png_get_interlace_type(png, m_reader->infoPtr())) { m_reader->createInterlaceBuffer(colorChannels * size().width() * size().height()); if (!m_reader->interlaceBuffer()) { longjmp(JMPBUF(png), 1); return; } } #if USE(QCMSLIB) if (m_reader->colorTransform()) { m_reader->createRowBuffer(colorChannels * size().width()); if (!m_reader->rowBuffer()) { longjmp(JMPBUF(png), 1); return; } } #endif buffer.setStatus(ImageFrame::FramePartial); buffer.setHasAlpha(false); // For PNGs, the frame always fills the entire image. buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); } /* libpng comments (here to explain what follows). * * this function is called for every row in the image. If the * image is interlacing, and you turned on the interlace handler, * this function will be called for every row in every pass. * Some of these rows will not be changed from the previous pass. * When the row is not changed, the new_row variable will be NULL. * The rows and passes are called in order, so you don't really * need the row_num and pass, but I'm supplying them because it * may make your life easier. */ // Nothing to do if the row is unchanged, or the row is outside // the image bounds: libpng may send extra rows, ignore them to // make our lives easier. if (!rowBuffer) return; int y = rowIndex; if (y < 0 || y >= size().height()) return; /* libpng comments (continued). * * For the non-NULL rows of interlaced images, you must call * png_progressive_combine_row() passing in the row and the * old row. You can call this function for NULL rows (it will * just return) and for non-interlaced images (it just does the * memcpy for you) if it will make the code easier. Thus, you * can just do this for all cases: * * png_progressive_combine_row(png_ptr, old_row, new_row); * * where old_row is what was displayed for previous rows. Note * that the first pass (pass == 0 really) will completely cover * the old row, so the rows do not have to be initialized. After * the first pass (and only for interlaced images), you will have * to pass the current row, and the function will combine the * old row and the new row. */ bool hasAlpha = m_reader->hasAlpha(); png_bytep row = rowBuffer; if (png_bytep interlaceBuffer = m_reader->interlaceBuffer()) { unsigned colorChannels = hasAlpha ? 4 : 3; row = interlaceBuffer + (rowIndex * colorChannels * size().width()); png_progressive_combine_row(m_reader->pngPtr(), row, rowBuffer); } #if USE(QCMSLIB) if (qcms_transform* transform = m_reader->colorTransform()) { qcms_transform_data(transform, row, m_reader->rowBuffer(), size().width()); row = m_reader->rowBuffer(); } #endif // Write the decoded row pixels to the frame buffer. The repetitive // form of the row write loops is for speed. ImageFrame::PixelData* address = buffer.getAddr(0, y); unsigned alphaMask = 255; int width = size().width(); png_bytep pixel = row; if (hasAlpha) { if (buffer.premultiplyAlpha()) { for (int x = 0; x < width; ++x, pixel += 4) { buffer.setRGBAPremultiply(address++, pixel[0], pixel[1], pixel[2], pixel[3]); alphaMask &= pixel[3]; } } else { for (int x = 0; x < width; ++x, pixel += 4) { buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], pixel[3]); alphaMask &= pixel[3]; } } } else { for (int x = 0; x < width; ++x, pixel += 3) { buffer.setRGBARaw(address++, pixel[0], pixel[1], pixel[2], 255); } } if (alphaMask != 255 && !buffer.hasAlpha()) buffer.setHasAlpha(true); buffer.setPixelsChanged(true); }
int simpl_gray_load_png(SimplGrayImage **image, SimplInStream istream, const SimplColorToGrayMethods method, const SimplPixel bk_value) { png_structp png_ptr; png_infop info_ptr; png_byte color_type, bitdepth, *row_data=NULL; png_bytep* row_ptrs=NULL; png_uint_32 i, j, x, width, height, row_size; SimplColorPixel *pixels=NULL; SimplPixel *iptr, *alpha=NULL; int value, out = SIMPL_INTERNAL; /* Create a read struct. */ if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) { return SIMPL_INTERNAL; } /* Create an info struct. */ if (!(info_ptr = png_create_info_struct(png_ptr))) { goto error; } /* Handle libpng errors with a magic setjmp. */ if (setjmp(png_jmpbuf(png_ptr))) { goto error; } /* Set the stream-based data source. */ png_set_read_fn(png_ptr, istream, StreamReadData); /* Read the info chunk. */ png_read_info(png_ptr, info_ptr); /* Get the dimensions and color information. */ width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); bitdepth = png_get_bit_depth(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); /* If palette, low bit depth gray, transparent w/o alpha, or 16 bit, fix it. */ if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); color_type = png_get_color_type(png_ptr, info_ptr); } else if (color_type == PNG_COLOR_TYPE_GRAY) { if (bitdepth<8) png_set_expand_gray_1_2_4_to_8(png_ptr); bitdepth = 8; } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); color_type = png_get_color_type(png_ptr, info_ptr); } png_set_strip_16(png_ptr); /* Either allocate a row, or load the entire image into a buffer. This is * because single row access uses less memory but doesn't work for * interlaced PNGs. */ row_size = png_get_rowbytes(png_ptr, info_ptr); if (png_get_interlace_type(png_ptr, info_ptr)==PNG_INTERLACE_NONE) { row_data = (png_byte *)malloc(sizeof(png_byte) * row_size); if (!row_data) { out = SIMPL_NOMEM; goto error; } } else { row_ptrs = (png_bytep*)calloc(height, sizeof(png_bytep)); if (!row_ptrs) { out = SIMPL_NOMEM; goto error; } for (j=0; j<height; ++j) { row_ptrs[j] = (png_byte *)malloc(sizeof(png_byte) * row_size); if (!row_ptrs[j]) { out = SIMPL_NOMEM; goto error; } } png_read_image(png_ptr, row_ptrs); } /* Allocate an image of the specified size. */ out = simpl_gray(image, width, height); if (out != SIMPL_OK) goto error; /* Decode the image line by line. */ if (color_type == PNG_COLOR_TYPE_RGB || color_type == PNG_COLOR_TYPE_RGB_ALPHA) { pixels = (SimplColorPixel *)malloc(sizeof(SimplColorPixel) * width); if (!pixels) { out = SIMPL_NOMEM; goto error; } alpha = (SimplPixel *)malloc(sizeof(SimplPixel) * width); if (!alpha) { out = SIMPL_NOMEM; goto error; } memset(alpha, 255, width); for (j=0; j<height; j++) { if (row_ptrs) row_data = row_ptrs[j]; else png_read_row(png_ptr, row_data, NULL); iptr = (*image)->image + j * width; if (color_type==PNG_COLOR_TYPE_RGB) { for (x=i=0; x<width; ++x, i+=3) { pixels[x].red = row_data[i]; pixels[x].green = row_data[i+1]; pixels[x].blue = row_data[i+2]; } } else { for (x=i=0; x<width; ++x, i+=4) { pixels[x].red = row_data[i]; pixels[x].green = row_data[i+1]; pixels[x].blue = row_data[i+2]; alpha[x] = row_data[i+3]; } } switch(method) { case COLOR_TO_GRAY_RED: for (i=0; i<width; ++i) iptr[i] = pixels[i].red; break; case COLOR_TO_GRAY_GREEN: for (i=0; i<width; ++i) iptr[i] = pixels[i].green; break; case COLOR_TO_GRAY_BLUE: for (i=0; i<width; ++i) iptr[i] = pixels[i].blue; break; case COLOR_TO_GRAY_ALPHA: for (i=0; i<width; ++i) iptr[i] = alpha[i]; break; case COLOR_TO_GRAY_CIE: if (color_type==PNG_COLOR_TYPE_RGB_ALPHA) { for (i=0; i<width; ++i) { value = (6969*pixels[i].red + 23434*pixels[i].green + 2365*pixels[i].blue)>>15; value = (alpha[i]*value + (255-alpha[i])*bk_value) / 255; iptr[i] = (value>255)?255:value; } } else { for (i=0; i<width; ++i) { value = (6969*pixels[i].red + 23434*pixels[i].green + 2365*pixels[i].blue)>>15; iptr[i] = (value>255)?255:value; } } break; case COLOR_TO_GRAY_MEAN: default: if (color_type==PNG_COLOR_TYPE_RGB_ALPHA) { for (i=0; i<width; ++i) { value = (pixels[i].red + pixels[i].green + pixels[i].blue)/3; value = (alpha[i]*value + (255-alpha[i])*bk_value) / 255; iptr[i] = (value>255)?255:value; } } else { for (i=0; i<width; ++i) { value = (pixels[i].red + pixels[i].green + pixels[i].blue)/3; iptr[i] = (value>255)?255:value; } } break; } }
int simpl_image_load_png(SimplImage **image, SimplInStream istream) { png_structp png_ptr; png_infop info_ptr; png_byte color_type, bitdepth, *row_data=NULL; png_bytep* row_ptrs=NULL; png_uint_32 i, j, width, height, row_size; SimplColorPixel *iptr; SimplPixel *aptr; int out = SIMPL_INTERNAL; /* Create a read struct. */ if (!(png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0))) { return SIMPL_INTERNAL; } /* Create an info struct. */ if (!(info_ptr = png_create_info_struct(png_ptr))) { goto error; } /* Handle libpng errors with a magic setjmp. */ if (setjmp(png_jmpbuf(png_ptr))) { goto error; } /* Set the stream-based data source. */ png_set_read_fn(png_ptr, istream, StreamReadData); /* Read the info chunk. */ png_read_info(png_ptr, info_ptr); /* Get the dimensions and color information. */ width = png_get_image_width(png_ptr, info_ptr); height = png_get_image_height(png_ptr, info_ptr); bitdepth = png_get_bit_depth(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); /* If palette, low bit depth gray, transparent w/o alpha, or 16 bit, fix it. */ if (color_type == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); color_type = png_get_color_type(png_ptr, info_ptr); } else if (color_type == PNG_COLOR_TYPE_GRAY) { if (bitdepth<8) png_set_expand_gray_1_2_4_to_8(png_ptr); bitdepth = 8; } if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { png_set_tRNS_to_alpha(png_ptr); color_type = png_get_color_type(png_ptr, info_ptr); } png_set_strip_16(png_ptr); /* Either allocate a row, or load the entire image into a buffer. This is * because single row access uses less memory but doesn't work for * interlaced PNGs. */ row_size = png_get_rowbytes(png_ptr, info_ptr); if (png_get_interlace_type(png_ptr, info_ptr)==PNG_INTERLACE_NONE) { row_data = (png_byte *)malloc(sizeof(png_byte) * row_size); if (!row_data) { out = SIMPL_NOMEM; goto error; } } else { row_ptrs = (png_bytep*)calloc(height, sizeof(png_bytep)); if (!row_ptrs) { out = SIMPL_NOMEM; goto error; } for (j=0; j<height; ++j) { row_ptrs[j] = (png_byte *)malloc(sizeof(png_byte) * row_size); if (!row_ptrs[j]) { out = SIMPL_NOMEM; goto error; } } png_read_image(png_ptr, row_ptrs); } /* Allocate an image of the specified size. */ out = simpl_image(image, width, height); if (out != SIMPL_OK) goto error; /* Store the decoded image into our format. */ if (color_type == PNG_COLOR_TYPE_RGB) { for (j=0; j<height; j++) { if (row_ptrs) row_data = row_ptrs[j]; else png_read_row(png_ptr, row_data, NULL); iptr = (*image)->image + j * width; for (i=0; i<3*width; i+=3) { iptr->red = row_data[i]; iptr->green = row_data[i+1]; iptr->blue = row_data[i+2]; iptr++; } } } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) { simpl_alpha_create(*image); for (j=0; j<height; j++) { if (row_ptrs) row_data = row_ptrs[j]; else png_read_row(png_ptr, row_data, NULL); iptr = (*image)->image + j * width; aptr = (*image)->alpha + j * width; for (i=0; i<4*width; i+=4) { iptr->red = row_data[i]; iptr->green = row_data[i+1]; iptr->blue = row_data[i+2]; iptr++; *aptr++ = row_data[i+3]; } } } else if (color_type == PNG_COLOR_TYPE_GRAY) { for (j=0; j<height; j++) { if (row_ptrs) row_data = row_ptrs[j]; else png_read_row(png_ptr, row_data, NULL); iptr = (*image)->image + j * width; for (i=0; i<width; i++) { iptr->red = row_data[i]; iptr->green = row_data[i]; iptr->blue = row_data[i]; iptr++; } } } else if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) { simpl_alpha_create(*image); for (j=0; j<height; j++) { if (row_ptrs) row_data = row_ptrs[j]; else png_read_row(png_ptr, row_data, NULL); iptr = (*image)->image + j * width; aptr = (*image)->alpha + j * width; for (i=0; i<2*width; i+=2) { iptr->red = row_data[i]; iptr->green = row_data[i]; iptr->blue = row_data[i]; iptr++; *aptr++ = row_data[i+1]; } } } else goto error; error: if (row_ptrs) { for (j=0; j<height; ++j) { if (row_ptrs[j]) free((void *)row_ptrs[j]); } free((void *)row_ptrs); } else { if (row_data) free((void *)row_data); } png_destroy_read_struct(&png_ptr, &info_ptr, 0); return out; }
int xps_decode_png(xps_context_t *ctx, byte *rbuf, int rlen, xps_image_t *image) { png_structp png; png_infop info; struct xps_png_io_s io; int npasses; int pass; int y; /* * Set up PNG structs and input source */ io.ptr = rbuf; io.lim = rbuf + rlen; png = png_create_read_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, ctx->memory, xps_png_malloc, xps_png_free); if (!png) return gs_throw(-1, "png_create_read_struct"); info = png_create_info_struct(png); if (!info) return gs_throw(-1, "png_create_info_struct"); png_set_read_fn(png, &io, xps_png_read); png_set_crc_action(png, PNG_CRC_WARN_USE, PNG_CRC_WARN_USE); /* * Jump to here on errors. */ if (setjmp(png_jmpbuf(png))) { png_destroy_read_struct(&png, &info, NULL); return gs_throw(-1, "png reading failed"); } /* * Read PNG header */ png_read_info(png, info); if (png_get_interlace_type(png, info) == PNG_INTERLACE_ADAM7) { npasses = png_set_interlace_handling(png); } else { npasses = 1; } if (png_get_color_type(png, info) == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png); } if (png_get_valid(png, info, PNG_INFO_tRNS)) { /* this will also expand the depth to 8-bits */ png_set_tRNS_to_alpha(png); } png_read_update_info(png, info); image->width = png_get_image_width(png, info); image->height = png_get_image_height(png, info); image->comps = png_get_channels(png, info); image->bits = png_get_bit_depth(png, info); /* See if we have an icc profile */ if (info->iccp_profile != NULL) { image->profilesize = info->iccp_proflen; image->profile = xps_alloc(ctx, info->iccp_proflen); if (image->profile) { /* If we can't create it, just ignore */ memcpy(image->profile, info->iccp_profile, info->iccp_proflen); } } switch (png_get_color_type(png, info)) { case PNG_COLOR_TYPE_GRAY: image->colorspace = ctx->gray; image->hasalpha = 0; break; case PNG_COLOR_TYPE_RGB: image->colorspace = ctx->srgb; image->hasalpha = 0; break; case PNG_COLOR_TYPE_GRAY_ALPHA: image->colorspace = ctx->gray; image->hasalpha = 1; break; case PNG_COLOR_TYPE_RGB_ALPHA: image->colorspace = ctx->srgb; image->hasalpha = 1; break; default: return gs_throw(-1, "cannot handle this png color type"); } /* * Extract DPI, default to 96 dpi */ image->xres = 96; image->yres = 96; if (info->valid & PNG_INFO_pHYs) { png_uint_32 xres, yres; int unit; png_get_pHYs(png, info, &xres, &yres, &unit); if (unit == PNG_RESOLUTION_METER) { image->xres = xres * 0.0254 + 0.5; image->yres = yres * 0.0254 + 0.5; } } /* * Read rows, filling transformed output into image buffer. */ image->stride = (image->width * image->comps * image->bits + 7) / 8; image->samples = xps_alloc(ctx, image->stride * image->height); for (pass = 0; pass < npasses; pass++) { for (y = 0; y < image->height; y++) { png_read_row(png, image->samples + (y * image->stride), NULL); } } /* * Clean up memory. */ png_destroy_read_struct(&png, &info, NULL); return gs_okay; }
TypedImage LoadPng(std::istream& source) { #ifdef HAVE_PNG //so First, we validate our stream with the validate function I just mentioned if (!pango_png_validate(source)) { throw std::runtime_error("Not valid PNG header"); } //set up initial png structs png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, &PngWarningsCallback); if (!png_ptr) { throw std::runtime_error( "PNG Init error 1" ); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); throw std::runtime_error( "PNG Init error 2" ); } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); throw std::runtime_error( "PNG Init error 3" ); } png_set_read_fn(png_ptr,(png_voidp)&source, pango_png_stream_read); png_set_sig_bytes(png_ptr, PNGSIGSIZE); // Setup transformation options if( png_get_bit_depth(png_ptr, info_ptr) == 1) { //Unpack bools to bytes to ease loading. png_set_packing(png_ptr); } else if( png_get_bit_depth(png_ptr, info_ptr) < 8) { //Expand nonbool colour depths up to 8bpp png_set_expand_gray_1_2_4_to_8(png_ptr); } //Get rid of palette, by transforming it to RGB if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } //read the file png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_SWAP_ENDIAN, NULL); if( png_get_interlace_type(png_ptr,info_ptr) != PNG_INTERLACE_NONE) { throw std::runtime_error( "Interlace not yet supported" ); } const size_t w = png_get_image_width(png_ptr,info_ptr); const size_t h = png_get_image_height(png_ptr,info_ptr); const size_t pitch = png_get_rowbytes(png_ptr, info_ptr); TypedImage img(w, h, PngFormat(png_ptr, info_ptr), pitch); png_bytepp rows = png_get_rows(png_ptr, info_ptr); for( unsigned int r = 0; r < h; r++) { memcpy( img.ptr + pitch*r, rows[r], pitch ); } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return img; #else PANGOLIN_UNUSED(source); throw std::runtime_error("Rebuild Pangolin for PNG support."); #endif // HAVE_PNG }
void PNGImageDecoder::rowAvailable(unsigned char* rowBuffer, unsigned rowIndex, int interlacePass) { if (m_frameBufferCache.isEmpty()) return; // Initialize the framebuffer if needed. ImageFrame& buffer = m_frameBufferCache[0]; if (buffer.status() == ImageFrame::FrameEmpty) { if (!buffer.setSize(scaledSize().width(), scaledSize().height())) { longjmp(JMPBUF(m_reader->pngPtr()), 1); return; } buffer.setStatus(ImageFrame::FramePartial); buffer.setHasAlpha(false); buffer.setColorProfile(m_colorProfile); // For PNGs, the frame always fills the entire image. buffer.setOriginalFrameRect(IntRect(IntPoint(), size())); if (png_get_interlace_type(m_reader->pngPtr(), m_reader->infoPtr()) != PNG_INTERLACE_NONE) m_reader->createInterlaceBuffer((m_reader->hasAlpha() ? 4 : 3) * size().width() * size().height()); } if (!rowBuffer) return; // libpng comments (pasted in here to explain what follows) /* * this function is called for every row in the image. If the * image is interlacing, and you turned on the interlace handler, * this function will be called for every row in every pass. * Some of these rows will not be changed from the previous pass. * When the row is not changed, the new_row variable will be NULL. * The rows and passes are called in order, so you don't really * need the row_num and pass, but I'm supplying them because it * may make your life easier. * * For the non-NULL rows of interlaced images, you must call * png_progressive_combine_row() passing in the row and the * old row. You can call this function for NULL rows (it will * just return) and for non-interlaced images (it just does the * memcpy for you) if it will make the code easier. Thus, you * can just do this for all cases: * * png_progressive_combine_row(png_ptr, old_row, new_row); * * where old_row is what was displayed for previous rows. Note * that the first pass (pass == 0 really) will completely cover * the old row, so the rows do not have to be initialized. After * the first pass (and only for interlaced images), you will have * to pass the current row, and the function will combine the * old row and the new row. */ png_structp png = m_reader->pngPtr(); bool hasAlpha = m_reader->hasAlpha(); unsigned colorChannels = hasAlpha ? 4 : 3; png_bytep row; png_bytep interlaceBuffer = m_reader->interlaceBuffer(); if (interlaceBuffer) { row = interlaceBuffer + (rowIndex * colorChannels * size().width()); png_progressive_combine_row(png, row, rowBuffer); } else row = rowBuffer; // Copy the data into our buffer. int width = scaledSize().width(); int destY = scaledY(rowIndex); // Check that the row is within the image bounds. LibPNG may supply an extra row. if (destY < 0 || destY >= scaledSize().height()) return; bool nonTrivialAlpha = false; for (int x = 0; x < width; ++x) { png_bytep pixel = row + (m_scaled ? m_scaledColumns[x] : x) * colorChannels; unsigned alpha = hasAlpha ? pixel[3] : 255; buffer.setRGBA(x, destY, pixel[0], pixel[1], pixel[2], alpha); nonTrivialAlpha |= alpha < 255; } if (nonTrivialAlpha && !buffer.hasAlpha()) buffer.setHasAlpha(nonTrivialAlpha); }
int ImageFilter_PNG::ident(FileObject &file, IMAGE &img) { #ifdef HAVE_PNG try { const char *address=file.map(0,256); file.seek(0); if (address==NULL) return 0; if (png_sig_cmp((png_byte*)address, 0, 8)!=0) { // Ist es ein PNG-File? return 0; } png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL ,NULL, NULL); if (!png_ptr) return 0; png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr,(png_infopp)NULL, (png_infopp)NULL); return 0; } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return 0; } png_set_read_fn(png_ptr,&file, (png_rw_ptr) user_read_data); //png_set_write_fn(png_structp write_ptr, voidp write_io_ptr, png_rw_ptr write_data_fn, // png_flush_ptr output_flush_fn); png_read_info(png_ptr, info_ptr); img.width=png_get_image_width(png_ptr, info_ptr); img.height=png_get_image_height(png_ptr, info_ptr); img.bitdepth=png_get_bit_depth(png_ptr, info_ptr); img.colors=0; img.pitch=png_get_rowbytes(png_ptr, info_ptr); //img->pfp.header_version=0; bool supported=true; img.format=RGBFormat::unknown; if (img.bitdepth!=8) supported=false; // Nur 8-Bit/Farbwert wird unterstützt switch (png_get_color_type(png_ptr, info_ptr)) { case PNG_COLOR_TYPE_GRAY: img.bitdepth=8; img.colors=256; img.format=RGBFormat::GREY8; break; case PNG_COLOR_TYPE_PALETTE: img.bitdepth=8; img.colors=256; img.format=RGBFormat::Palette; //supported=false; break; case PNG_COLOR_TYPE_RGB: img.colors=0xffffff; img.bitdepth=24; img.format=RGBFormat::X8R8G8B8; break; case PNG_COLOR_TYPE_RGB_ALPHA: img.colors=0xffffff; img.bitdepth=32; img.format=RGBFormat::A8R8G8B8; break; case PNG_COLOR_TYPE_GRAY_ALPHA: img.colors=256; img.bitdepth=32; img.format=RGBFormat::GREYALPHA32; break; }; if (png_get_interlace_type(png_ptr,info_ptr)!=PNG_INTERLACE_NONE) { // Interlaced wird nicht unterstützt supported=false; } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); if (!supported) { return 0; } return 1; } catch (...) { return 0; } return 0; #else return 0; #endif }
void png_reader::read(unsigned x0, unsigned y0,image_data_32& image) { FILE *fp=fopen(fileName_.c_str(),"rb"); if (!fp) throw image_reader_exception("cannot open image file "+fileName_); png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { fclose(fp); throw image_reader_exception("failed to allocate png_ptr"); } // catch errors in a custom way to avoid the need for setjmp png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn); png_infop info_ptr; try { info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr,0,0); fclose(fp); throw image_reader_exception("failed to create info_ptr"); } } catch (std::exception const& ex) { png_destroy_read_struct(&png_ptr,0,0); fclose(fp); throw; } png_set_read_fn(png_ptr, (png_voidp)fp, png_read_data); png_read_info(png_ptr, info_ptr); if (color_type_ == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY && bit_depth_ < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (bit_depth_ == 16) png_set_strip_16(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY || color_type_ == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); // quick hack -- only work in >=libpng 1.2.7 png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); //rgba double gamma; if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, 2.2, gamma); if (x0 == 0 && y0 == 0 && image.width() >= width_ && image.height() >= height_) { if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) { png_set_interlace_handling(png_ptr); // FIXME: libpng bug? // according to docs png_read_image // "..automatically handles interlacing, // so you don't need to call png_set_interlace_handling()" } png_read_update_info(png_ptr, info_ptr); // we can read whole image at once // alloc row pointers boost::scoped_array<png_byte*> rows(new png_bytep[height_]); for (unsigned i=0; i<height_; ++i) rows[i] = (png_bytep)image.getRow(i); png_read_image(png_ptr, rows.get()); } else { png_read_update_info(png_ptr, info_ptr); unsigned w=std::min(unsigned(image.width()),width_); unsigned h=std::min(unsigned(image.height()),height_); unsigned rowbytes=png_get_rowbytes(png_ptr, info_ptr); boost::scoped_array<png_byte> row(new png_byte[rowbytes]); //START read image rows for (unsigned i=0;i<height_;++i) { png_read_row(png_ptr,row.get(),0); if (i>=y0 && i<h) { image.setRow(i-y0,reinterpret_cast<unsigned*>(&row[x0]),w); } } //END } png_read_end(png_ptr,0); png_destroy_read_struct(&png_ptr, &info_ptr,0); fclose(fp); }
int png_read(struct image *img){ unsigned int y; png_bytepp row_pointers; struct png_t *p = (struct png_t *)img; if(setjmp(png_jmpbuf(p->png_ptr))){ png_destroy_read_struct(&p->png_ptr, &p->info_ptr, &p->end_info); return 1; } { int color_type = png_get_color_type(p->png_ptr, p->info_ptr); int bit_depth = png_get_bit_depth(p->png_ptr, p->info_ptr); if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(p->png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(p->png_ptr); if (png_get_valid(p->png_ptr, p->info_ptr, PNG_INFO_tRNS)) png_set_expand(p->png_ptr); if (bit_depth == 16) png_set_strip_16(p->png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(p->png_ptr); //png_set_strip_alpha(p->png_ptr); png_color_16 my_background = {.red = 0xff, .green = 0xff, .blue = 0xff}; png_color_16p image_background; if(png_get_bKGD(p->png_ptr, p->info_ptr, &image_background)) png_set_background(p->png_ptr, image_background, PNG_BACKGROUND_GAMMA_FILE, 1, 1.0); else png_set_background(p->png_ptr, &my_background, PNG_BACKGROUND_GAMMA_SCREEN, 2, 1.0); if(png_get_interlace_type(p->png_ptr, p->info_ptr) == PNG_INTERLACE_ADAM7) p->numpasses = png_set_interlace_handling(p->png_ptr); else p->numpasses = 1; png_read_update_info(p->png_ptr, p->info_ptr); } row_pointers = (png_bytepp)malloc(img->bufheight * sizeof(png_bytep)); for(y = 0; y < img->bufheight; y++) row_pointers[y] = img->buf + y * img->bufwidth * 3; png_read_image(p->png_ptr, row_pointers); free(row_pointers); img->state |= LOADED | SLOWLOADED; return 0; } void png_close(struct image *img){ struct png_t *p = (struct png_t *)img; png_destroy_read_struct(&p->png_ptr, &p->info_ptr, &p->end_info); fclose(p->f); } struct imageformat libpng = { png_open, NULL, png_read, png_close };
TypedImage LoadPng(const std::string& filename) { PANGOLIN_UNUSED(filename); #ifdef HAVE_PNG FILE *in = fopen(filename.c_str(), "rb"); if( in ) { //check the header const size_t nBytes = 8; png_byte header[nBytes]; size_t nread = fread(header, 1, nBytes, in); int nIsPNG = png_sig_cmp(header, 0, nread); if ( nIsPNG != 0 ) { throw std::runtime_error( filename + " is not a PNG file" ); } //set up initial png structs png_structp png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, &PngWarningsCallback); if (!png_ptr) { throw std::runtime_error( "PNG Init error 1" ); } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); throw std::runtime_error( "PNG Init error 2" ); } png_infop end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); throw std::runtime_error( "PNG Init error 3" ); } png_init_io(png_ptr, in); png_set_sig_bytes(png_ptr, nBytes); //read the file png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_SWAP_ENDIAN, NULL); if( png_get_bit_depth(png_ptr, info_ptr) == 1) { //Unpack bools to bytes to ease loading. png_set_packing(png_ptr); } else if( png_get_bit_depth(png_ptr, info_ptr) < 8) { //Expand nonbool colour depths up to 8bpp png_set_expand_gray_1_2_4_to_8(png_ptr); } //Get rid of palette, by transforming it to RGB if(png_get_color_type(png_ptr, info_ptr) == PNG_COLOR_TYPE_PALETTE) { png_set_palette_to_rgb(png_ptr); } if( png_get_interlace_type(png_ptr,info_ptr) != PNG_INTERLACE_NONE) { throw std::runtime_error( "Interlace not yet supported" ); } const size_t w = png_get_image_width(png_ptr,info_ptr); const size_t h = png_get_image_height(png_ptr,info_ptr); const size_t pitch = png_get_rowbytes(png_ptr, info_ptr); TypedImage img(w, h, PngFormat(png_ptr, info_ptr), pitch); png_bytepp rows = png_get_rows(png_ptr, info_ptr); for( unsigned int r = 0; r < h; r++) { memcpy( img.ptr + pitch*r, rows[r], pitch ); } png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); fclose(in); return img; } throw std::runtime_error("Unable to load PNG file, '" + filename + "'"); #else throw std::runtime_error("PNG Support not enabled. Please rebuild Pangolin."); #endif }
PremultipliedImage decodePNG(const uint8_t* data, size_t size) { util::CharArrayBuffer dataBuffer { reinterpret_cast<const char*>(data), size }; std::istream stream(&dataBuffer); png_byte header[8] = { 0 }; stream.read(reinterpret_cast<char*>(header), 8); if (stream.gcount() != 8) throw std::runtime_error("PNG reader: Could not read image"); int is_png = !png_sig_cmp(header, 0, 8); if (!is_png) throw std::runtime_error("File or stream is not a png"); png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!png_ptr) throw std::runtime_error("failed to allocate png_ptr"); // catch errors in a custom way to avoid the need for setjmp png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn); png_infop info_ptr; png_struct_guard sguard(&png_ptr, &info_ptr); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) throw std::runtime_error("failed to create info_ptr"); png_set_read_fn(png_ptr, &stream, png_read_data); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_uint_32 width = 0; png_uint_32 height = 0; int bit_depth = 0; int color_type = 0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, nullptr, nullptr, nullptr); UnassociatedImage image { static_cast<uint16_t>(width), static_cast<uint16_t>(height) }; if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); png_set_add_alpha(png_ptr, 0xff, PNG_FILLER_AFTER); if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) { png_set_interlace_handling(png_ptr); // FIXME: libpng bug? // according to docs png_read_image // "..automatically handles interlacing, // so you don't need to call png_set_interlace_handling()" } png_read_update_info(png_ptr, info_ptr); // we can read whole image at once // alloc row pointers const std::unique_ptr<png_bytep[]> rows(new png_bytep[height]); for (unsigned row = 0; row < height; ++row) rows[row] = image.data.get() + row * width * 4; png_read_image(png_ptr, rows.get()); png_read_end(png_ptr, nullptr); return util::premultiply(std::move(image)); }
int begin(char* base_file_name, unsigned char *png_buf, long long png_length) { MY_PNG_READ_OFFSET = 0; PNG_LENGTH = png_length; ENTIRE_PNG_BUF = png_buf; if (png_sig_cmp(ENTIRE_PNG_BUF, 0, 8) != 0) { error(-1, "png_sig_cmp", E_INVALID); return -1; } DEBUG_PRINT(("Initial png size is %lld bytes\n", PNG_LENGTH)); my_png_meta *pm = calloc(1, sizeof(my_png_meta)); my_init_libpng(pm); //If libpng errors, we end up here if (setjmp(png_jmpbuf(pm->read_ptr))) { DEBUG_PRINT(("libpng called setjmp!\n")); my_deinit_libpng(pm); free(ENTIRE_PNG_BUF); error(-1, "libpng", "libpng encountered an error\n"); return -1; } //Normally a file, but instead make it our buffer void *read_io_ptr = png_get_io_ptr(pm->read_ptr); png_set_read_fn(pm->read_ptr, read_io_ptr, my_png_read_fn); //Transform all PNG image types to RGB int transforms = PNG_TRANSFORM_GRAY_TO_RGB | PNG_TRANSFORM_STRIP_ALPHA | PNG_TRANSFORM_EXPAND; png_read_png(pm->read_ptr, pm->info_ptr, transforms, NULL); //Now that it was read and transformed, its size will differ PNG_LENGTH = 0; //Lets collect our metadata struct ihdr_infos_s ihdr_infos; ihdr_infos.bit_depth = png_get_bit_depth(pm->read_ptr, pm->info_ptr); ihdr_infos.color_type = png_get_color_type(pm->read_ptr, pm->info_ptr); ihdr_infos.filter_method = png_get_filter_type(pm->read_ptr, pm->info_ptr); ihdr_infos.compression_type = png_get_compression_type(pm->read_ptr, pm->info_ptr); ihdr_infos.interlace_type = png_get_interlace_type(pm->read_ptr, pm->info_ptr); ihdr_infos.height = png_get_image_height(pm->read_ptr, pm->info_ptr); ihdr_infos.width = png_get_image_width(pm->read_ptr, pm->info_ptr); if (ihdr_infos.color_type != 2) { DEBUG_PRINT((E_INVALID)); free(ENTIRE_PNG_BUF); my_deinit_libpng(pm); DEBUG_PRINT(("Looks like libpng could not correctly convert to RGB\n")); return -1; } //Just in case we want to enable alpha, etc switch(ihdr_infos.color_type) { case 0: //greyscale case 3: //indexed ihdr_infos.bytes_per_pixel = 1; break; case 4: ihdr_infos.bytes_per_pixel = 2; break; //greyscale w/ alpha case 2: ihdr_infos.bytes_per_pixel = 3; break; //Truecolour (RGB) case 6: ihdr_infos.bytes_per_pixel = 4; break; //Truecolour w/ alpha default: error_fatal(1, "ihdr_infos", "Unknown image type"); //should never happen } ihdr_infos.scanline_len = (ihdr_infos.bytes_per_pixel * ihdr_infos.width) + 1; DEBUG_PRINT(("HEIGHT: %u\n", ihdr_infos.height)); DEBUG_PRINT(("WIDTH: %u\n", ihdr_infos.width)); DEBUG_PRINT(("BIT_DEPTH: %u\n", ihdr_infos.bit_depth)); // Don't compress, since we are merely copying it to memory, // we will be decompressing it again anyway png_set_compression_level(pm->write_ptr, Z_NO_COMPRESSION); void *write_io_ptr = png_get_io_ptr(pm->write_ptr); png_set_write_fn(pm->write_ptr, write_io_ptr, my_png_write_fn, my_png_dummy_flush); //Make sure we use all filters png_set_filter(pm->write_ptr, 0, PNG_FILTER_NONE | PNG_FILTER_VALUE_NONE | PNG_FILTER_SUB | PNG_FILTER_VALUE_SUB | PNG_FILTER_UP | PNG_FILTER_VALUE_UP | PNG_FILTER_AVG | PNG_FILTER_VALUE_AVG | PNG_FILTER_PAETH | PNG_FILTER_VALUE_PAETH); //Set our comment struct png_text_struct comment_struct; comment_struct.compression = -1; comment_struct.key = " Glitched by pnglitch.xyz "; comment_struct.text = NULL; comment_struct.text_length = 0; png_set_text(pm->write_ptr, pm->info_ptr, &comment_struct, 1); //Buffer is Written using callback my_png_write_fn to buffer //ENTIRE_PNG_BUF. PNG_LENGTH will be updated automatically by it png_write_png(pm->write_ptr, pm->info_ptr, PNG_TRANSFORM_IDENTITY, NULL); my_deinit_libpng(pm); DEBUG_PRINT(("libpng output buf is %lld bytes\n", PNG_LENGTH)); //Now that libpng has converted the image //and we have it in a buffer, we process it by hand with zlib struct z_stream_s inflate_stream; my_init_zlib(&inflate_stream); inflateInit(&inflate_stream); //Pointer to keep track of where we are unsigned char *pngp = ENTIRE_PNG_BUF; //Skip PNG Signature pngp += 8; //Get Header unsigned char ihdr_bytes_buf[4+4+13+4]; // size + label + content + crc buf_read(ihdr_bytes_buf, &pngp, 4+4+13+4); //When we run into non-idat chunks, we will want to preserve them. //The spec says there's no chunk that needs to go after IDAT, //so we can simply concatenate all of these chunks into a buffer //then write them all at once after the IHDR //ancillary chunks, eg comments unsigned char *ancil_chunks_buf = calloc(1,1); long long ancil_chunks_len = 0; unsigned char *unzip_idats_buf = calloc(1, 1); long unzip_buf_len = 1; long unzip_buf_offset = 0; long long zipped_idats_len = 0; //Length of all idats as we read them unsigned long accum_png_len = 8 + (4+4+13+4); int chunk_count = 0; printf("Uncompressing image data...\n"); while (1) { unsigned char chunk_label[4]; unsigned char chunk_len_buf[4]; buf_read(chunk_len_buf, &pngp, 4); //first 4 bytes are the length of data section long chunk_len = four_bytes_to_int(chunk_len_buf); accum_png_len += chunk_len + 4 + 4 + 4; // plus len, crc, label DEBUG_PRINT(("at %lu --> %lld\n", accum_png_len, PNG_LENGTH)); //leave at end of buffer if (accum_png_len >= PNG_LENGTH) break; //read the chunk label (name of this header) buf_read(chunk_label, &pngp, 4); DEBUG_PRINT(("Reading chunk %d with label '%c%c%c%c', size %ld\n", chunk_count, chunk_label[0], chunk_label[1], chunk_label[2], chunk_label[3], chunk_len)); chunk_count += 1; if (memcmp(chunk_label, "IDAT", 4) == 0) { zipped_idats_len += chunk_len; //read the chunk's data section unsigned char *raw_chunk_buf = calloc(chunk_len, 1); buf_read(raw_chunk_buf, &pngp, chunk_len); //Tell inflate to uncompress it inflate_stream.next_in = raw_chunk_buf; inflate_stream.avail_in = chunk_len; //Now uncompress it (resizes buffer automatically) unsigned char *check_uncompress = uncompress_buffer(&inflate_stream, unzip_idats_buf, &unzip_buf_len, &unzip_buf_offset); //Stop if error if (check_uncompress == NULL) { DEBUG_PRINT((E_GLITCH)); free(ancil_chunks_buf); free(raw_chunk_buf); free(unzip_idats_buf); free(ENTIRE_PNG_BUF); return -1; } //Moving on unzip_idats_buf = check_uncompress; free(raw_chunk_buf); pngp += 4; // skip CRC } else { //This is not an idat ancil_chunks_buf = realloc(ancil_chunks_buf, ancil_chunks_len + 4 + 4 + chunk_len + 4); //make room for new data //append length and label bytes append_bytes(ancil_chunks_buf, chunk_len_buf, &ancil_chunks_len, 4); append_bytes(ancil_chunks_buf, chunk_label, &ancil_chunks_len, 4); //append chunk data unsigned char *raw_chunk_buf = calloc(chunk_len, 1); buf_read(raw_chunk_buf, &pngp, chunk_len); append_bytes(ancil_chunks_buf, raw_chunk_buf, &ancil_chunks_len, chunk_len ); //append chunk crc unsigned char chunk_crc_buf[4]; buf_read(chunk_crc_buf, &pngp, 4); append_bytes(ancil_chunks_buf, chunk_crc_buf, &ancil_chunks_len, 4); free(raw_chunk_buf); DEBUG_PRINT(("ancillary chunks length: %lld\n", ancil_chunks_len)); } } //buf contains all idats uncompressed, concatenated unsigned long unzipped_idats_len = inflate_stream.total_out; unzip_idats_buf = realloc(unzip_idats_buf, unzipped_idats_len); //we already have ancillary chunks and idats, don't need the original free(ENTIRE_PNG_BUF); inflateEnd(&inflate_stream); printf("Uncompressed %lld bytes to %ld bytes\n", zipped_idats_len, unzipped_idats_len); printf("Glitching image data...\n"); for (int g=0;g<NUM_OUTPUT_FILES;g++) { //do glitches switch(g) { case 5: glitch_random(unzip_idats_buf, unzipped_idats_len, ihdr_infos.scanline_len, 0.0005); break; case 6: glitch_random_filter(unzip_idats_buf, unzipped_idats_len, ihdr_infos.scanline_len); break; default: glitch_filter(unzip_idats_buf, unzipped_idats_len, ihdr_infos.scanline_len, g); } //recompress so we can write them to file long long glitched_idats_len = 0; unsigned char *glitched_idats = zip_idats(unzip_idats_buf, unzipped_idats_len, &glitched_idats_len); if (glitched_idats == NULL) { DEBUG_PRINT((E_GLITCH)); free (unzip_idats_buf); free (ancil_chunks_buf); return -1; } char path[MAX_PATH_LENGTH]; bzero(path, MAX_PATH_LENGTH); snprintf(path, MAX_PATH_LENGTH, "%s%s%s-%d.png", OUTPUT_DIRECTORY, DIR_SEP, base_file_name, g); DEBUG_PRINT(("Output file name is %s\n", path)); FILE *outfp = fopen(path, "wb"); write_glitched_image(glitched_idats, glitched_idats_len, ihdr_bytes_buf, ancil_chunks_buf, ancil_chunks_len, outfp); printf("%s\n", path); fflush(stdout); fclose(outfp); free(glitched_idats); } free(ancil_chunks_buf); free(unzip_idats_buf); return 0; }
void png_reader<T>::read(unsigned x0, unsigned y0,image_data_32& image) { stream_.clear(); stream_.seekg(0, std::ios_base::beg); png_structp png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) { throw image_reader_exception("failed to allocate png_ptr"); } // catch errors in a custom way to avoid the need for setjmp png_set_error_fn(png_ptr, png_get_error_ptr(png_ptr), user_error_fn, user_warning_fn); png_infop info_ptr; png_struct_guard sguard(&png_ptr,&info_ptr); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) throw image_reader_exception("failed to create info_ptr"); png_set_read_fn(png_ptr, (png_voidp)&stream_, png_read_data); png_read_info(png_ptr, info_ptr); if (color_type_ == PNG_COLOR_TYPE_PALETTE) png_set_expand(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY && bit_depth_ < 8) png_set_expand(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_expand(png_ptr); if (bit_depth_ == 16) png_set_strip_16(png_ptr); if (color_type_ == PNG_COLOR_TYPE_GRAY || color_type_ == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); // quick hack -- only work in >=libpng 1.2.7 png_set_add_alpha(png_ptr,0xff,PNG_FILLER_AFTER); //rgba double gamma; if (png_get_gAMA(png_ptr, info_ptr, &gamma)) png_set_gamma(png_ptr, 2.2, gamma); if (x0 == 0 && y0 == 0 && image.width() >= width_ && image.height() >= height_) { if (png_get_interlace_type(png_ptr,info_ptr) == PNG_INTERLACE_ADAM7) { png_set_interlace_handling(png_ptr); // FIXME: libpng bug? // according to docs png_read_image // "..automatically handles interlacing, // so you don't need to call png_set_interlace_handling()" } png_read_update_info(png_ptr, info_ptr); // we can read whole image at once // alloc row pointers const std::unique_ptr<png_bytep[]> rows(new png_bytep[height_]); for (unsigned i=0; i<height_; ++i) rows[i] = (png_bytep)image.getRow(i); png_read_image(png_ptr, rows.get()); } else { png_read_update_info(png_ptr, info_ptr); unsigned w=std::min(unsigned(image.width()),width_ - x0); unsigned h=std::min(unsigned(image.height()),height_ - y0); unsigned rowbytes=png_get_rowbytes(png_ptr, info_ptr); const std::unique_ptr<png_byte[]> row(new png_byte[rowbytes]); //START read image rows for (unsigned i = 0;i < height_; ++i) { png_read_row(png_ptr,row.get(),0); if (i >= y0 && i < (y0 + h)) { image.setRow(i-y0,reinterpret_cast<unsigned*>(&row[x0 * 4]),w); } } //END } png_read_end(png_ptr,0); }