/*! \internal */ void Q_INTERNAL_WIN_NO_THROW QPngHandlerPrivate::readPngTexts(png_info *info) { #ifndef QT_NO_IMAGE_TEXT png_textp text_ptr; int num_text=0; png_get_text(png_ptr, info, &text_ptr, &num_text); while (num_text--) { QString key, value; key = QString::fromLatin1(text_ptr->key); #if defined(PNG_iTXt_SUPPORTED) if (text_ptr->itxt_length) { value = QString::fromUtf8(text_ptr->text, int(text_ptr->itxt_length)); } else #endif { value = QString::fromLatin1(text_ptr->text, int(text_ptr->text_length)); } if (!description.isEmpty()) description += QLatin1String("\n\n"); description += key + QLatin1String(": ") + value.simplified(); readTexts.append(key); readTexts.append(value); text_ptr++; } #endif }
bool emPngImageFileModel::TryContinueLoading() throw(emString) { png_textp t; int e,i,n; if (!L->ImagePrepared) { Image.Setup( L->width, L->height, L->bytes_per_pixel ); Signal(ChangeSignal); L->ImagePrepared=true; return false; } if (setjmp(L->jmpbuffer)) throw emString(L->errorText); if (L->y<(int)L->height && L->pass<L->number_of_passes) { png_read_row( L->png_ptr, Image.GetWritableMap()+L->y*Image.GetWidth()*Image.GetChannelCount(), NULL ); L->y++; if (L->y>=(int)L->height) { L->y=0; L->pass++; } Signal(ChangeSignal); return false; } png_read_end(L->png_ptr,L->end_info_ptr); for (e=0; e<2; e++) { n=png_get_text( L->png_ptr, e ? L->end_info_ptr : L->info_ptr, &t, NULL ); for (i=0; i<n; i++) { if ( t[i].text && *t[i].text && t[i].key && ( strcasecmp(t[i].key,"Comment")==0 || strcasecmp(t[i].key,"Description")==0 ) ) { if (!Comment.IsEmpty()) Comment+='\n'; Comment+=t[i].text; } } } Signal(ChangeSignal); return true; }
/** \brief Get depth. * * Note that this function is expensive as depth is stored in comments. * * @return Depth as unsigned or 0. */ unsigned getDepth() const { png_textp text_array; int text_count; if(png_get_text(m_png, m_info, &text_array, &text_count) > 0) { for(int ii = 0; (ii < text_count); ++ii) { png_textp text = text_array + ii; if(PNG_TEXT_DEPTH_KEYWORD.compare(text->key) == 0) { return boost::lexical_cast<unsigned>(text->text); } } } return 0; // No depth available equals 0. }
void get_text(struct PNGImage *png) { png_text *text; int i, numtxts, numremoved; png_get_text(png->png, png->info, &text, &numtxts); for (i = 0; i < numtxts; i++) { if (strcmp(text[i].key, "h") == 0 && !*text[i].text) { png->horizontal = true; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "x") == 0) { png->trim = strtoul(text[i].text, NULL, 0); png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "t") == 0) { png->mapfile = text[i].text; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "T") == 0 && !*text[i].text) { png->mapout = true; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "p") == 0) { png->palfile = text[i].text; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } else if (strcmp(text[i].key, "P") == 0 && !*text[i].text) { png->palout = true; png_free_data(png->png, png->info, PNG_FREE_TEXT, i); } } /* TODO: Remove this and simply change the warning function not to warn instead. */ for (i = 0, numremoved = 0; i < numtxts; i++) { if (text[i].key == NULL) { numremoved++; } text[i].key = text[i + numremoved].key; text[i].text = text[i + numremoved].text; text[i].compression = text[i + numremoved].compression; } png_set_text(png->png, png->info, text, numtxts - numremoved); }
static BOOL ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { // XMP keyword const char *g_png_xmp_keyword = "XML:com.adobe.xmp"; FITAG *tag = NULL; png_textp text_ptr = NULL; int num_text = 0; // iTXt/tEXt/zTXt chuncks if(png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) > 0) { for(int i = 0; i < num_text; i++) { // create a tag tag = FreeImage_CreateTag(); if(!tag) return FALSE; DWORD tag_length = (DWORD) MAX(text_ptr[i].text_length, text_ptr[i].itxt_length); FreeImage_SetTagLength(tag, tag_length); FreeImage_SetTagCount(tag, tag_length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, text_ptr[i].text); if(strcmp(text_ptr[i].key, g_png_xmp_keyword) == 0) { // store the tag as XMP FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); } else { // store the tag as a comment FreeImage_SetTagKey(tag, text_ptr[i].key); FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); } // destroy the tag FreeImage_DeleteTag(tag); } } return TRUE; }
/* PNGのテキストチャンクからオフセットを読み込み、offsetにセットする関数 ※画像データの読み書きの前に呼び出さないといけない @cspecs テキストチャンクにアクセスするために必要 @offset セットするwoff_t */ static void getOffsetFromChunk(woff_t *offset, struct png_control_specs *cspecs) { png_text *text; int num_comments, num_text; /* コメントの取得 */ num_comments = png_get_text(cspecs->png_ptr, cspecs->info_ptr, &text, &num_text); printf("num_comments : %d\n", num_comments); if(num_comments == 0){ return; } #ifdef DEBUG printf("[key words]\n%s\n", text->key); printf("[text body]\n%s\n", text->text); #endif offset->total_bit = atoi(text->text); calcPixelOffset(offset, cspecs->shared_specs); /* plane_no, x, y, colorを計算する */ }
void PNG::dump() { // Text chunks png_textp textp = 0; int ntext = 0; png_get_text(mData, mInfo, &textp, &ntext); var text; for (int i=0; i<ntext; i++) text[textp[i].key] = textp[i].text; std::cout << "Text: " << text << std::endl; // EXIF // The library reports "png_get_eXIf does not work; use png_get_eXIf_1" png_bytep exif_data = 0; png_uint_32 num_exif = 0; png_uint_32 pngRet = png_get_eXIf_1(mData, mInfo, &num_exif, &exif_data); if (pngRet) { assert(exif_data); EXIF exif(exif_data, num_exif); exif.dump(); std::cout << "EXIF date: " << exif.date() << std::endl; } }
static void _png_load_bmp_attribute(png_structp png_ptr, png_infop info_ptr, CFX_DIBAttribute* pAttribute) { if (pAttribute) { #if defined(PNG_pHYs_SUPPORTED) pAttribute->m_nXDPI = png_get_x_pixels_per_meter(png_ptr, info_ptr); pAttribute->m_nYDPI = png_get_y_pixels_per_meter(png_ptr, info_ptr); png_uint_32 res_x, res_y; int unit_type; png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type); switch (unit_type) { case PNG_RESOLUTION_METER: pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_METER; break; default: pAttribute->m_wDPIUnit = FXCODEC_RESUNIT_NONE; } #endif #if defined(PNG_iCCP_SUPPORTED) png_charp icc_name; png_bytep icc_profile; png_uint_32 icc_proflen; int compress_type; png_get_iCCP(png_ptr, info_ptr, &icc_name, &compress_type, &icc_profile, &icc_proflen); #endif int bTime = 0; #if defined(PNG_tIME_SUPPORTED) png_timep t = nullptr; png_get_tIME(png_ptr, info_ptr, &t); if (t) { FXSYS_memset(pAttribute->m_strTime, 0, sizeof(pAttribute->m_strTime)); FXSYS_snprintf((FX_CHAR*)pAttribute->m_strTime, sizeof(pAttribute->m_strTime), "%4u:%2u:%2u %2u:%2u:%2u", t->year, t->month, t->day, t->hour, t->minute, t->second); pAttribute->m_strTime[sizeof(pAttribute->m_strTime) - 1] = 0; bTime = 1; } #endif #if defined(PNG_TEXT_SUPPORTED) int i; FX_STRSIZE len; const FX_CHAR* buf; int num_text; png_textp text = nullptr; png_get_text(png_ptr, info_ptr, &text, &num_text); for (i = 0; i < num_text; i++) { len = FXSYS_strlen(text[i].key); buf = "Time"; if (!FXSYS_memcmp(buf, text[i].key, std::min(len, FXSYS_strlen(buf)))) { if (!bTime) { FXSYS_memset(pAttribute->m_strTime, 0, sizeof(pAttribute->m_strTime)); FXSYS_memcpy( pAttribute->m_strTime, text[i].text, std::min(sizeof(pAttribute->m_strTime) - 1, text[i].text_length)); } } else { buf = "Author"; if (!FXSYS_memcmp(buf, text[i].key, std::min(len, FXSYS_strlen(buf)))) { pAttribute->m_strAuthor = CFX_ByteString(reinterpret_cast<uint8_t*>(text[i].text), static_cast<FX_STRSIZE>(text[i].text_length)); } } } #endif } }
gboolean xdg_cache_cache_read_thumbnail_info (const gchar *filename, gchar **uri, guint64 *mtime, GCancellable *cancellable, GError **error) { png_structp png_ptr; png_infop info_ptr; png_textp text_ptr; gboolean has_uri = FALSE; gboolean has_mtime = FALSE; FILE *png; gint num_text; gint i; g_return_val_if_fail (filename != NULL, FALSE); g_return_val_if_fail (uri != NULL, FALSE); g_return_val_if_fail (mtime != NULL, FALSE); g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), FALSE); g_return_val_if_fail (error == NULL || *error == NULL, FALSE); *uri = NULL; *mtime = 0; if (g_cancellable_set_error_if_cancelled (cancellable, error)) return FALSE; if ((png = g_fopen (filename, "r")) != NULL) { /* initialize the PNG reader */ png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr) { /* initialize the info structure */ info_ptr = png_create_info_struct (png_ptr); if (info_ptr) { #ifdef PNG_SETJMP_SUPPORTED if (setjmp (png_jmpbuf (png_ptr))) { /* finalize the PNG reader */ png_destroy_read_struct (&png_ptr, &info_ptr, NULL); /* close the PNG file handle */ fclose (png); return FALSE; } #endif /* initialize reading from the file and read the file info */ png_init_io (png_ptr, png); png_read_info (png_ptr, info_ptr); /* check if there is embedded text information */ if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_text) > 0) { /* iterate over all text keys */ for (i = 0; !(has_uri && has_mtime) && i < num_text; ++i) { if (!text_ptr[i].key) continue; if (strcmp ("Thumb::URI", text_ptr[i].key) == 0) { /* remember the Thumb::URI value */ *uri = g_strdup (text_ptr[i].text); has_uri = TRUE; } else if (strcmp ("Thumb::MTime", text_ptr[i].key) == 0) { /* remember the Thumb::MTime value */ if (text_ptr[i].text != NULL) { *mtime = atol (text_ptr[i].text); has_mtime = TRUE; } } } } } /* finalize the PNG reader */ png_destroy_read_struct (&png_ptr, &info_ptr, NULL); } /* close the PNG file handle */ fclose (png); } return TRUE; }
/** * @brief Opens an npng struct from an SDL_RWops. It does not close the RWops. * * @param rw SDL_RWops to create npng from. * @return npng created from rw. */ npng_t *npng_open( SDL_RWops *rw ) { png_byte header[8]; /* Maximum size to check. */ npng_t *npng; /* Allocate memory. */ npng = malloc( sizeof(npng_t) ); if (npng == NULL) { WARN("Out of memory."); return NULL; } memset( npng, 0, sizeof(npng_t) ); /* Set up struct. */ npng->rw = rw; npng->png_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL ); if (npng->png_ptr == NULL) { WARN("png_create_read_struct failed"); goto ERR_FAIL; } npng->info_ptr = png_create_info_struct( npng->png_ptr ); if (npng->info_ptr == NULL) { WARN("png_create_info_struct failed"); goto ERR_FAIL; } /* Check header. */ SDL_RWread( rw, header, 8, 1 ); if (png_sig_cmp(header, 0, 8)) { WARN("RWops not recognized as a PNG file."); goto ERR_FAIL; } /* Set up for reading. */ png_set_read_fn( npng->png_ptr, (png_voidp) rw, npng_read ); /* Set up long jump for IO. */ if (setjmp( png_jmpbuf( npng->png_ptr )) ) { WARN("Error during setjmp"); goto ERR_FAIL; } /* We've already checked sig. */ png_set_sig_bytes( npng->png_ptr, 8 ); /* Get start. */ npng->start = SDL_RWtell( npng->rw ); /* Get info. */ npng_info( npng ); /* Load text. */ png_get_text( npng->png_ptr, npng->info_ptr, &npng->text_ptr, &npng->num_text ); return npng; ERR_FAIL: #if 0 /* Memory leaks are better than segfaults. */ if (npng != NULL) { if (npng->png_ptr != NULL) png_destroy_read_struct( &npng->png_ptr, (npng->info_ptr != NULL) ? &npng->info_ptr : NULL, NULL ); free(npng); } #endif return NULL; }
/*! * pixReadStreamPng() * * Input: stream * Return: pix, or null on error * * Notes: * (1) If called from pixReadStream(), the stream is positioned * at the beginning of the file. * (2) To do sequential reads of png format images from a stream, * use pixReadStreamPng() */ PIX * pixReadStreamPng(FILE *fp) { l_uint8 rval, gval, bval; l_int32 i, j, k; l_int32 wpl, d, spp, cindex; l_uint32 png_transforms; l_uint32 *data, *line, *ppixel; int num_palette, num_text; png_byte bit_depth, color_type, channels; png_uint_32 w, h, rowbytes; png_uint_32 xres, yres; png_bytep rowptr; png_bytep *row_pointers; png_structp png_ptr; png_infop info_ptr, end_info; png_colorp palette; png_textp text_ptr; /* ptr to text_chunk */ PIX *pix; PIXCMAP *cmap; PROCNAME("pixReadStreamPng"); if (!fp) return (PIX *)ERROR_PTR("fp not defined", procName, NULL); pix = NULL; /* Allocate the 3 data structures */ if ((png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL)) == NULL) return (PIX *)ERROR_PTR("png_ptr not made", procName, NULL); if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); return (PIX *)ERROR_PTR("info_ptr not made", procName, NULL); } if ((end_info = png_create_info_struct(png_ptr)) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return (PIX *)ERROR_PTR("end_info not made", procName, NULL); } /* Set up png setjmp error handling */ if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (PIX *)ERROR_PTR("internal png error", procName, NULL); } png_init_io(png_ptr, fp); /* ---------------------------------------------------------- * * Set the transforms flags. Whatever happens here, * NEVER invert 1 bpp using PNG_TRANSFORM_INVERT_MONO. * ---------------------------------------------------------- */ /* To strip 16 --> 8 bit depth, use PNG_TRANSFORM_STRIP_16 */ if (var_PNG_STRIP_16_TO_8 == 1) /* our default */ png_transforms = PNG_TRANSFORM_STRIP_16; else png_transforms = PNG_TRANSFORM_IDENTITY; /* To remove alpha channel, use PNG_TRANSFORM_STRIP_ALPHA */ if (var_PNG_STRIP_ALPHA == 1) /* our default */ png_transforms |= PNG_TRANSFORM_STRIP_ALPHA; /* Read it */ png_read_png(png_ptr, info_ptr, png_transforms, NULL); row_pointers = png_get_rows(png_ptr, info_ptr); w = png_get_image_width(png_ptr, info_ptr); h = png_get_image_height(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); rowbytes = png_get_rowbytes(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); channels = png_get_channels(png_ptr, info_ptr); spp = channels; if (spp == 1) d = bit_depth; else if (spp == 2) { d = 2 * bit_depth; L_WARNING("there shouldn't be 2 spp!", procName); } else /* spp == 3 (rgb), spp == 4 (rgba) */ d = 4 * bit_depth; /* Remove if/when this is implemented for all bit_depths */ if (spp == 3 && bit_depth != 8) { fprintf(stderr, "Help: spp = 3 and depth = %d != 8\n!!", bit_depth); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (PIX *)ERROR_PTR("not implemented for this depth", procName, NULL); } if (color_type == PNG_COLOR_TYPE_PALETTE || color_type == PNG_COLOR_MASK_PALETTE) { /* generate a colormap */ png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette); cmap = pixcmapCreate(d); /* spp == 1 */ for (cindex = 0; cindex < num_palette; cindex++) { rval = palette[cindex].red; gval = palette[cindex].green; bval = palette[cindex].blue; pixcmapAddColor(cmap, rval, gval, bval); } } else cmap = NULL; if ((pix = pixCreate(w, h, d)) == NULL) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return (PIX *)ERROR_PTR("pix not made", procName, NULL); } wpl = pixGetWpl(pix); data = pixGetData(pix); pixSetColormap(pix, cmap); if (spp == 1) { /* copy straight from buffer to pix */ for (i = 0; i < h; i++) { line = data + i * wpl; rowptr = row_pointers[i]; for (j = 0; j < rowbytes; j++) { SET_DATA_BYTE(line, j, rowptr[j]); } } } else { /* spp == 3 or spp == 4 */ for (i = 0; i < h; i++) { ppixel = data + i * wpl; rowptr = row_pointers[i]; for (j = k = 0; j < w; j++) { SET_DATA_BYTE(ppixel, COLOR_RED, rowptr[k++]); SET_DATA_BYTE(ppixel, COLOR_GREEN, rowptr[k++]); SET_DATA_BYTE(ppixel, COLOR_BLUE, rowptr[k++]); if (spp == 4) SET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL, rowptr[k++]); ppixel++; } } } #if DEBUG if (cmap) { for (i = 0; i < 16; i++) { fprintf(stderr, "[%d] = %d\n", i, ((l_uint8 *)(cmap->array))[i]); } } #endif /* DEBUG */ /* If there is no colormap, PNG defines black = 0 and * white = 1 by default for binary monochrome. Therefore, * since we use the opposite definition, we must invert * the image colors in either of these cases: * (i) there is no colormap (default) * (ii) there is a colormap which defines black to * be 0 and white to be 1. * We cannot use the PNG_TRANSFORM_INVERT_MONO flag * because that flag (since version 1.0.9) inverts 8 bpp * grayscale as well, which we don't want to do. * (It also doesn't work if there is a colormap.) * If there is a colormap that defines black = 1 and * white = 0, we don't need to do anything. * * How do we check the polarity of the colormap? * The colormap determines the values of black and * white pixels in the following way: * if black = 1 (255), white = 0 * 255, 255, 255, 0, 0, 0, 0, 0, 0 * if black = 0, white = 1 (255) * 0, 0, 0, 0, 255, 255, 255, 0 * So we test the first byte to see if it is 0; * if so, invert the colors. * * If there is a colormap, after inverting the pixels it is * necessary to destroy the colormap. Otherwise, if someone were * to call pixRemoveColormap(), this would cause the pixel * values to be inverted again! */ if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { /* fprintf(stderr, "Inverting binary data on png read\n"); */ pixInvert(pix, pix); pixDestroyColormap(pix); } xres = png_get_x_pixels_per_meter(png_ptr, info_ptr); yres = png_get_y_pixels_per_meter(png_ptr, info_ptr); pixSetXRes(pix, (l_int32)((l_float32)xres / 39.37 + 0.5)); /* to ppi */ pixSetYRes(pix, (l_int32)((l_float32)yres / 39.37 + 0.5)); /* to ppi */ /* Get the text if there is any */ png_get_text(png_ptr, info_ptr, &text_ptr, &num_text); if (num_text && text_ptr) pixSetText(pix, text_ptr->text); png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); return pix; }
s32 pngDecodeData(ppu_thread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null) { // Indicate, that the PNG decoding is stopped/failed. This is incase, we return an error code in the middle of decoding data_out_info->status = CELL_PNGDEC_DEC_STATUS_STOP; const u32 bytes_per_line = data_control_param->outputBytesPerLine; // Log this for now if (bytes_per_line < stream->out_param.outputWidthByte) { fmt::throw_exception("Bytes per line less than expected output! Got: %d, expected: %d" HERE, bytes_per_line, stream->out_param.outputWidthByte); } // partial decoding if (cb_control_disp && stream->outputCounts > 0) { // get data from cb auto streamInfo = vm::ptr<CellPngDecStrmInfo>::make(handle->malloc_(ppu, sizeof(CellPngDecStrmInfo), handle->malloc_arg).addr()); auto streamParam = vm::ptr<CellPngDecStrmParam>::make(handle->malloc_(ppu, sizeof(CellPngDecStrmParam), handle->malloc_arg).addr()); stream->cbDispInfo = vm::ptr<CellPngDecDispInfo>::make(handle->malloc_(ppu, sizeof(CellPngDecDispInfo), handle->malloc_arg).addr()); stream->cbDispParam = vm::ptr<CellPngDecDispParam>::make(handle->malloc_(ppu, sizeof(CellPngDecDispParam), handle->malloc_arg).addr()); auto freeMem = [&]() { handle->free_(ppu, streamInfo, handle->free_arg); handle->free_(ppu, streamParam, handle->free_arg); handle->free_(ppu, stream->cbDispInfo, handle->free_arg); handle->free_(ppu, stream->cbDispParam, handle->free_arg); }; // set things that won't change between callbacks stream->cbDispInfo->outputFrameWidthByte = bytes_per_line; stream->cbDispInfo->outputFrameHeight = stream->out_param.outputHeight; stream->cbDispInfo->outputWidthByte = stream->out_param.outputWidthByte; stream->cbDispInfo->outputBitDepth = stream->out_param.outputBitDepth; stream->cbDispInfo->outputComponents = stream->out_param.outputComponents; stream->cbDispInfo->outputHeight = stream->outputCounts; stream->cbDispInfo->outputStartXByte = 0; stream->cbDispInfo->outputStartY = 0; stream->cbDispInfo->scanPassCount = 0; stream->cbDispInfo->nextOutputStartY = 0; stream->ppuContext = &ppu; stream->nextRow = stream->cbDispInfo->outputHeight; stream->cbCtrlDisp.cbCtrlDispArg = cb_control_disp->cbCtrlDispArg; stream->cbCtrlDisp.cbCtrlDispFunc = cb_control_disp->cbCtrlDispFunc; stream->cbDispParam->nextOutputImage = disp_param->nextOutputImage; streamInfo->decodedStrmSize = stream->buffer->cursor; // push the rest of the buffer we have if (stream->buffer->length > stream->buffer->cursor) { u8* data = static_cast<u8*>(stream->buffer->data.get_ptr()) + stream->buffer->cursor; try { png_process_data(stream->png_ptr, stream->info_ptr, data, stream->buffer->length - stream->buffer->cursor); } catch (LibPngCustomException&) { freeMem(); return CELL_PNGDEC_ERROR_FATAL; } streamInfo->decodedStrmSize = stream->buffer->length; } // todo: commandPtr // then just loop until the end, the callbacks should take care of the rest while (stream->endOfFile != true) { stream->cbCtrlStream.cbCtrlStrmFunc(ppu, streamInfo, streamParam, stream->cbCtrlStream.cbCtrlStrmArg); streamInfo->decodedStrmSize += streamParam->strmSize; try { png_process_data(stream->png_ptr, stream->info_ptr, static_cast<u8*>(streamParam->strmPtr.get_ptr()), streamParam->strmSize); } catch (LibPngCustomException&) { freeMem(); return CELL_PNGDEC_ERROR_FATAL; } } freeMem(); } else { // Check if the image needs to be flipped const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP; // Decode the image // todo: commandptr try { for (int j = 0; j < stream->passes; j++) { for (int i = 0; i < stream->out_param.outputHeight; ++i) { const u32 line = flip ? stream->out_param.outputHeight - i - 1 : i; png_read_row(stream->png_ptr, &data[line*bytes_per_line], nullptr); } } png_read_end(stream->png_ptr, stream->info_ptr); } catch (LibPngCustomException&) { return CELL_PNGDEC_ERROR_FATAL; } } // Get the number of iTXt, tEXt and zTXt chunks const s32 text_chunks = png_get_text(stream->png_ptr, stream->info_ptr, nullptr, nullptr); // Set the chunk information and the previously obtained number of text chunks data_out_info->numText = (u32)text_chunks; data_out_info->chunkInformation = pngDecGetChunkInformation(stream.get_ptr(), true); png_unknown_chunkp unknowns; const int num_unknowns = png_get_unknown_chunks(stream->png_ptr, stream->info_ptr, &unknowns); data_out_info->numUnknownChunk = num_unknowns; // Indicate that the decoding succeeded data_out_info->status = CELL_PNGDEC_DEC_STATUS_FINISH; return CELL_OK; }
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; }
static void load_meta_data(png_structp png, png_infop png_info, gp_storage *storage) { double gamma; png_uint_32 res_x, res_y, w, h; int unit, depth, color_type, interlace_type, compr_method; const char *type; png_get_IHDR(png, png_info, &w, &h, &depth, &color_type, &interlace_type, &compr_method, NULL); gp_storage_add_string(storage, NULL, "Interlace Type", interlace_type_name(interlace_type)); gp_storage_add_int(storage, NULL, "Width", w); gp_storage_add_int(storage, NULL, "Height", h); gp_storage_add_int(storage, NULL, "Bit Depth", depth); if (color_type & PNG_COLOR_MASK_PALETTE) { type = "Palette"; } else { if (color_type & PNG_COLOR_MASK_COLOR) type = "RGB"; else type = "Grayscale"; } gp_storage_add_string(storage, NULL, "Color Type", type); //TODO: To string gp_storage_add_int(storage, NULL, "Compression Method", compr_method); /* TODO: BOOL ? */ gp_storage_add_string(storage, NULL, "Alpha Channel", color_type & PNG_COLOR_MASK_ALPHA ? "Yes" : "No"); if (png_get_gAMA(png, png_info, &gamma)) gp_storage_add_int(storage, NULL, "gamma", gamma * 100000); if (png_get_pHYs(png, png_info, &res_x, &res_y, &unit)) { gp_storage_add_int(storage, NULL, "X Resolution", res_x); gp_storage_add_int(storage, NULL, "Y Resolution", res_y); const char *str_unit; if (unit == PNG_RESOLUTION_METER) str_unit = "Meter"; else str_unit = "Unknown"; gp_storage_add_string(storage, NULL, "Resolution Unit", str_unit); } png_timep mod_time; if (png_get_tIME(png, png_info, &mod_time)) { char buf[128]; snprintf(buf, sizeof(buf), "%4i:%02i:%02i %02i:%02i:%02i", mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second); gp_storage_add_string(storage, NULL, "Date Time", buf); } png_textp text_ptr; int text_cnt; if (png_get_text(png, png_info, &text_ptr, &text_cnt)) { int i; char buf[128]; for (i = 0; i < text_cnt; i++) { if (text_ptr[i].compression != PNG_TEXT_COMPRESSION_NONE) continue; snprintf(buf, sizeof(buf), "Text %03i", i); gp_storage_add_string(storage, NULL, buf, text_ptr[i].text); } } }
SEXP read_png(SEXP sFn, SEXP sNative, SEXP sInfo) { SEXP res = R_NilValue, info_list = R_NilValue, info_tail = R_NilValue; const char *fn; char header[8]; int native = asInteger(sNative), info = (asInteger(sInfo) == 1); FILE *f; read_job_t rj; png_structp png_ptr; png_infop info_ptr; if (TYPEOF(sFn) == RAWSXP) { rj.data = (char*) RAW(sFn); rj.len = LENGTH(sFn); rj.ptr = 0; rj.f = f = 0; } else { if (TYPEOF(sFn) != STRSXP || LENGTH(sFn) < 1) Rf_error("invalid filename"); fn = CHAR(STRING_ELT(sFn, 0)); f = fopen(fn, "rb"); if (!f) Rf_error("unable to open %s", fn); if (fread(header, 1, 8, f) < 1 || png_sig_cmp((png_bytep) header, 0, 8)) { fclose(f); Rf_error("file is not in PNG format"); } rj.f = f; } /* use our own error hanlding code and pass the fp so it can be closed on error */ png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)&rj, user_error_fn, user_warning_fn); if (!png_ptr) { if (f) fclose(f); Rf_error("unable to initialize libpng"); } info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { if (f) fclose(f); png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); Rf_error("unable to initialize libpng"); } if (f) { png_init_io(png_ptr, f); png_set_sig_bytes(png_ptr, 8); } else png_set_read_fn(png_ptr, (png_voidp) &rj, user_read_data); #define add_info(K, V) { info_tail = SETCDR(info_tail, CONS(V, R_NilValue)); SET_TAG(info_tail, install(K)); } /* png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_STRIP_16 | PNG_TRANSFORM_EXPAND, NULL); */ png_read_info(png_ptr, info_ptr); { png_uint_32 width, height; png_bytepp row_pointers; char *img_memory; SEXP dim; int bit_depth, color_type, interlace_type, compression_type, filter_method, rowbytes; int need_swap = 0; png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); rowbytes = png_get_rowbytes(png_ptr, info_ptr); #if VERBOSE_INFO Rprintf("png: %d x %d [%d], %d bytes, 0x%x, %d, %d\n", (int) width, (int) height, bit_depth, rowbytes, color_type, interlace_type, compression_type, filter_method); #endif if (info) { SEXP dv; double d; png_uint_32 rx, ry; int ut, num_text = 0; png_textp text_ptr; info_tail = info_list = PROTECT(CONS((dv = allocVector(INTSXP, 2)), R_NilValue)); INTEGER(dv)[0] = (int) width; INTEGER(dv)[1] = (int) height; SET_TAG(info_list, install("dim")); add_info("bit.depth", ScalarInteger(bit_depth)); switch(color_type) { case PNG_COLOR_TYPE_GRAY: add_info("color.type", mkString("gray")); break; case PNG_COLOR_TYPE_GRAY_ALPHA: add_info("color.type", mkString("gray + alpha")); break; case PNG_COLOR_TYPE_PALETTE: add_info("color.type", mkString("palette")); break; case PNG_COLOR_TYPE_RGB: add_info("color.type", mkString("RGB")); break; case PNG_COLOR_TYPE_RGB_ALPHA: add_info("color.type", mkString("RGBA")); break; default: add_info("color.type", ScalarInteger(color_type)); } if (png_get_gAMA(png_ptr, info_ptr, &d)) add_info("gamma", ScalarReal(d)); #ifdef PNG_pHYs_SUPPORTED if (png_get_pHYs(png_ptr, info_ptr, &rx, &ry, &ut)) { if (ut == PNG_RESOLUTION_METER) { dv = allocVector(REALSXP, 2); REAL(dv)[0] = ((double)rx) / 39.37008; REAL(dv)[1] = ((double)ry) / 39.37008; add_info("dpi", dv); } else if (ut == PNG_RESOLUTION_UNKNOWN) add_info("asp", ScalarReal(rx / ry)); } if (png_get_text(png_ptr, info_ptr, &text_ptr, &num_text)) { SEXP txt_key, txt_val = PROTECT(allocVector(STRSXP, num_text)); if (num_text) { int i; setAttrib(txt_val, R_NamesSymbol, txt_key = allocVector(STRSXP, num_text)); for (i = 0; i < num_text; i++) { SET_STRING_ELT(txt_val, i, text_ptr[i].text ? mkChar(text_ptr[i].text) : NA_STRING); SET_STRING_ELT(txt_key, i, text_ptr[i].key ? mkChar(text_ptr[i].key) : NA_STRING); } } add_info("text", txt_val); UNPROTECT(1); } #endif } /* on little-endian machines it's all well, but on big-endian ones we'll have to swap */ #if ! defined (__BIG_ENDIAN__) && ! defined (__LITTLE_ENDIAN__) /* old compiler so have to use run-time check */ { char bo[4] = { 1, 0, 0, 0 }; int bi; memcpy(&bi, bo, 4); if (bi != 1) need_swap = 1; } #endif #ifdef __BIG_ENDIAN__ need_swap = 1; #endif /*==== set any transforms that we desire: ====*/ /* palette->RGB - no discussion there */ if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); /* expand gray scale to 8 bits */ if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8) png_set_expand_gray_1_2_4_to_8(png_ptr); /* this should not be necessary but it's in the docs to guarantee 8-bit */ if (bit_depth < 8) png_set_packing(png_ptr); /* convert tRNS chunk into alpha */ if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) png_set_tRNS_to_alpha(png_ptr); /* native format doesn't allow for 16-bit so it needs to be truncated */ if (bit_depth == 16 && native) { Rf_warning("Image uses 16-bit channels but R native format only supports 8-bit, truncating LSB."); png_set_strip_16(png_ptr); } /* for native output we need to a) convert gray to RGB, b) add alpha */ if (native) { if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); if (!(color_type & PNG_COLOR_MASK_ALPHA)) /* if there is no alpha, add it */ png_set_add_alpha(png_ptr, 0xFF, PNG_FILLER_AFTER); } #if 0 /* we use native (network) endianness since we read each byte anyway */ /* on little-endian machines we need to swap 16-bit values - this is the inverse of need_swap as used for R! */ if (!need_swap && bit_depth == 16) png_set_swap(png_ptr); #endif /* PNG wants up to call png_set_interlace_handling so it can get ready to de-interlace images */ png_set_interlace_handling(png_ptr); /* all transformations are in place, so it's time to update the info structure so we can allocate stuff */ png_read_update_info(png_ptr, info_ptr); /* re-read some important bits from the updated structure */ rowbytes = png_get_rowbytes(png_ptr, info_ptr); bit_depth = png_get_bit_depth(png_ptr, info_ptr); color_type = png_get_color_type(png_ptr, info_ptr); #if VERBOSE_INFO Rprintf(" -filter-> %d-bits, %d bytes, 0x%x\n", bit_depth, rowbytes, color_type); #endif /* allocate data fro row pointers and the image using R's allocation */ row_pointers = (png_bytepp) R_alloc(height, sizeof(png_bytep)); img_memory = R_alloc(height, rowbytes); { /* populate the row pointers */ char *i_ptr = img_memory; int i; for (i = 0; i < height; i++, i_ptr += rowbytes) row_pointers[i] = (png_bytep) i_ptr; } /* do the reading work */ png_read_image(png_ptr, row_pointers); if (f) { rj.f = 0; fclose(f); } /* native output - vector of integers */ if (native) { int pln = rowbytes / width; if (pln < 1 || pln > 4) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); Rf_error("native output for %d planes is not possible.", pln); } res = PROTECT(allocVector(INTSXP, width * height)); if (pln == 4) { /* 4 planes - efficient - just copy it all */ int y, *idata = INTEGER(res); for (y = 0; y < height; idata += width, y++) memcpy(idata, row_pointers[y], width * sizeof(int)); if (need_swap) { int *ide = idata; idata = INTEGER(res); for (; idata < ide; idata++) RX_swap32(*idata); } } else if (pln == 3) { /* RGB */ int x, y, *idata = INTEGER(res); for (y = 0; y < height; y++) for (x = 0; x < rowbytes; x += 3) *(idata++) = R_RGB((unsigned int) row_pointers[y][x], (unsigned int) row_pointers[y][x + 1], (unsigned int) row_pointers[y][x + 2]); } else if (pln == 2) { /* GA */ int x, y, *idata = INTEGER(res); for (y = 0; y < height; y++) for (x = 0; x < rowbytes; x += 2) *(idata++) = R_RGBA((unsigned int) row_pointers[y][x], (unsigned int) row_pointers[y][x], (unsigned int) row_pointers[y][x], (unsigned int) row_pointers[y][x + 1]); } else { /* gray */ int x, y, *idata = INTEGER(res); for (y = 0; y < height; y++) for (x = 0; x < rowbytes; x++) *(idata++) = R_RGB((unsigned int) row_pointers[y][x], (unsigned int) row_pointers[y][x], (unsigned int) row_pointers[y][x]); } dim = allocVector(INTSXP, 2); INTEGER(dim)[0] = height; INTEGER(dim)[1] = width; setAttrib(res, R_DimSymbol, dim); setAttrib(res, R_ClassSymbol, mkString("nativeRaster")); setAttrib(res, install("channels"), ScalarInteger(pln)); UNPROTECT(1); } else { int x, y, p, pln = rowbytes / width, pls = width * height; double * data; if (bit_depth == 16) { res = PROTECT(allocVector(REALSXP, (rowbytes * height) / 2)); pln /= 2; } else res = PROTECT(allocVector(REALSXP, rowbytes * height)); data = REAL(res); if (bit_depth == 16) for(y = 0; y < height; y++) for (x = 0; x < width; x++) for (p = 0; p < pln; p++) data[y + x * height + p * pls] = ((double)( (((unsigned int)(((unsigned char *)row_pointers[y])[2 * (x * pln + p)])) << 8) | ((unsigned int)(((unsigned char *)row_pointers[y])[2 * (x * pln + p) + 1])) )) / 65535.0; else for(y = 0; y < height; y++) for (x = 0; x < width; x++) for (p = 0; p < pln; p++) data[y + x * height + p * pls] = ((double)row_pointers[y][x * pln + p]) / 255.0; dim = allocVector(INTSXP, (pln > 1) ? 3 : 2); INTEGER(dim)[0] = height; INTEGER(dim)[1] = width; if (pln > 1) INTEGER(dim)[2] = pln; setAttrib(res, R_DimSymbol, dim); UNPROTECT(1); } } if (info) { PROTECT(res); setAttrib(res, install("info"), info_list); UNPROTECT(2); } png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return res; }
/* create a new finfo record, open FITS file */ static Finfo FinfoNew(char *fname) { int len; char *e=NULL; char *f=NULL; char *s=NULL; #if FITS2PNG int i; unsigned char header[8]; /* use volatile to make gcc [-Wclobbered] happy (because of setjmp below) */ volatile Finfo finfo; #else Finfo finfo; #endif /* sanity check */ if( !fname ) return NULL; /* return existing finfo, if possible */ if( (finfo=FinfoLookup(fname)) ) return finfo; /* allocate record */ if( !(finfo = (Finfo)xcalloc(sizeof(FinfoRec), 1)) ){ fprintf(stderr, "ERROR: can't allocate rec for image\n"); return NULL; } /* save file name */ finfo->fname = xstrdup(fname); /* check for file type */ if( (s = strrchr(fname, '.')) && !strcasecmp(s, ".png") ){ /* its a PNG */ finfo->ftype = FTYPE_PNG; } else { /* assume FITS type */ finfo->ftype = FTYPE_FITS; } /* open file */ switch(finfo->ftype){ case FTYPE_PNG: #if FITS2PNG /* code taken from "PNG: The Definitive Guide" by Greg Roelofs, Chapter 13 "Reading PNG Images" */ /* set data path */ datapath = getenv("JS9_DATAPATH"); /* look for path of the PNG file */ s = Find(fname, "r", NULL, datapath); if( s && *s ){ if( !(finfo->fp = fopen(s, "rb")) ){ fprintf(stderr, "ERROR: can't open PNG file '%s'\n", fname); goto error; } fread(header, 1, 8, finfo->fp); if( png_sig_cmp(header, 0, 8) ){ fprintf(stderr, "ERROR: not recognized as a PNG file '%s'\n", fname); goto error; } /* initialize stuff */ finfo->png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if( !finfo->png_ptr ){ fprintf(stderr, "ERROR: png_create_read_struct failed '%s'\n", fname); goto error; } finfo->info_ptr = png_create_info_struct(finfo->png_ptr); if( !finfo->info_ptr ){ fprintf(stderr, "ERROR: png_create_info_struct failed '%s'\n", fname); goto error; } if( setjmp(png_jmpbuf(finfo->png_ptr)) ){ fprintf(stderr, "ERROR: during png init_io '%s'\n", fname); goto error; } png_init_io(finfo->png_ptr, finfo->fp); png_set_sig_bytes(finfo->png_ptr, 8); png_read_info(finfo->png_ptr, finfo->info_ptr); /* get the text chunks that come before the image */ if( png_get_text(finfo->png_ptr, finfo->info_ptr, &(finfo->text_ptr), &(finfo->num_text)) > 0 ){ /* process all known PNG keywords */ for(i=0; i<finfo->num_text; i++){ if( !strcmp(finfo->text_ptr[i].key, FITSFILE) ){ finfo->fitsfile = xstrdup(finfo->text_ptr[i].text); /* remove the extension that was used to generate png */ s = strchr(finfo->fitsfile, '['); if( s ){ *s = '\0'; } } } } } else { fprintf(stderr, "ERROR: can't find PNG file '%s' [data path: %s]\n", fname, datapath?datapath:"none"); goto error; } #else fprintf(stderr, "ERROR: for fits2png support, build JS9 using --with-png\n"); goto error; #endif break; /* look for an error */ case FTYPE_FITS: /* fits file can have an extension */ f = FileRoot(fname); /* set data path */ datapath = getenv("JS9_DATAPATH"); /* look for path of the FITS file */ s = Find(f, "r", NULL, datapath); xfree(f); if( s && *s ){ len = strlen(s) + 1; /* construct full path to fits file + extension */ e = FileExtension(fname); if( e ){ len += strlen(e); } finfo->fitsfile = xmalloc(len); strcpy(finfo->fitsfile, s); if( e ){ strcat(finfo->fitsfile, e); } xfree(s); } else { fprintf(stderr, "ERROR: can't find FITS file '%s' [data path: %s]\n", fname, datapath?datapath:"none"); goto error; } break; default: fprintf(stderr, "ERROR: unknown file type '%s'\n", fname); goto error; break; } /* add this finfo to end of list of existing finfos */ FinfoListAdd(&finfohead, finfo); /* return the news */ return finfo; error: /* free up struct and return nothing */ _FinfoFree(finfo); return NULL; }
int res_create_multi_display_surface(const char* name, int* frames, gr_surface** pSurface) { gr_surface* surface = NULL; int result = 0; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_uint_32 width, height; png_byte channels; int i; *pSurface = NULL; *frames = -1; result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels); if (result < 0) return result; *frames = 1; png_textp text; int num_text; if (png_get_text(png_ptr, info_ptr, &text, &num_text)) { for (i = 0; i < num_text; ++i) { if (text[i].key && strcmp(text[i].key, "Frames") == 0 && text[i].text) { *frames = atoi(text[i].text); break; } } printf(" found frames = %d\n", *frames); } if (height % *frames != 0) { printf("bad height (%d) for frame count (%d)\n", height, *frames); result = -9; goto exit; } surface = malloc(*frames * sizeof(gr_surface)); if (surface == NULL) { result = -8; goto exit; } for (i = 0; i < *frames; ++i) { surface[i] = NULL;//init_display_surface(width, height / *frames); if (surface[i] == NULL) { result = -8; goto exit; } } /*unsigned char* p_row = malloc(width * 4); unsigned int y; for (y = 0; y < height; ++y) { png_read_row(png_ptr, p_row, NULL); int frame = y % *frames; unsigned char* out_row = surface[frame]->data + (y / *frames) * surface[frame]->row_bytes; transform_rgb_to_draw(p_row, out_row, channels, width); } free(p_row); this will need to be brought in line with the older resources and graphics code */ *pSurface = (gr_surface*) surface; exit: png_destroy_read_struct(&png_ptr, &info_ptr, NULL); if (result < 0) { if (surface) { for (i = 0; i < *frames; ++i) { if (surface[i]) free(surface[i]); } free(surface); } } return result; }
/*! \internal */ bool QPngHandlerPrivate::readPngHeader() { state = Error; png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,0,0,0); if (!png_ptr) return false; png_set_error_fn(png_ptr, 0, 0, qt_png_warning); info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_read_struct(&png_ptr, 0, 0); png_ptr = 0; return false; } end_info = png_create_info_struct(png_ptr); if (!end_info) { png_destroy_read_struct(&png_ptr, &info_ptr, 0); png_ptr = 0; return false; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, &end_info); png_ptr = 0; return false; } png_set_read_fn(png_ptr, q->device(), iod_read_fn); png_read_info(png_ptr, info_ptr); #ifndef QT_NO_IMAGE_TEXT png_textp text_ptr; int num_text=0; png_get_text(png_ptr,info_ptr,&text_ptr,&num_text); while (num_text--) { QString key, value; #if defined(PNG_iTXt_SUPPORTED) if (text_ptr->lang) { QTextCodec *codec = QTextCodec::codecForName(text_ptr->lang); if (codec) { key = codec->toUnicode(text_ptr->lang_key); value = codec->toUnicode(QByteArray(text_ptr->text, text_ptr->itxt_length)); } else { key = QString::fromLatin1(text_ptr->key); value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length))); } } else #endif { key = QString::fromLatin1(text_ptr->key); value = QString::fromLatin1(QByteArray(text_ptr->text, int(text_ptr->text_length))); } if (!description.isEmpty()) description += QLatin1String("\n\n"); description += key + QLatin1String(": ") + value.simplified(); text_ptr++; } #endif state = ReadHeader; return true; }
GivImage *giv_plugin_load_file(const char *filename, GError **error) { GivImage *img=NULL; FILE *fp = fopen(filename, "rb"); if (!fp) return NULL; // Check again that this is a png file const int number = 8; guchar header[9]; fread(header, 1, number, fp); gboolean is_png = !png_sig_cmp(header, 0, number); if (!is_png) { return NULL; } png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL,NULL,NULL); if (!png_ptr) return NULL; // Comments may be stored in the beginning or the end so create // a structure for both. 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 NULL; } 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 NULL; } png_uint_32 width, height; int bit_depth, color_type, interlace_type, compression_type, filter_method; png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); #if 0 printf("width height bit_depth color_type = %d %d %d %d\n", (int)width, (int)height, bit_depth, color_type); #endif if (color_type == PNG_COLOR_TYPE_PALETTE) png_set_palette_to_rgb(png_ptr); // Since giv doesn't support gray alpha, we upgrade to rgb if (color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_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); if (bit_depth == 16) png_set_swap(png_ptr); // Reread info png_read_update_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, &interlace_type, &compression_type, &filter_method); GivImageType image_type; if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth <= 8) image_type = GIVIMAGE_U8; else if (color_type == PNG_COLOR_TYPE_GRAY && bit_depth == 16) image_type = GIVIMAGE_U16; else if (color_type == PNG_COLOR_TYPE_RGB && bit_depth == 16) image_type = GIVIMAGE_RGB_U16; else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA && bit_depth == 16) image_type = GIVIMAGE_RGBA_U16; else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA && bit_depth == 8) image_type = GIVIMAGE_RGBA_U8; else image_type = GIVIMAGE_RGB_U8; img = giv_image_new(image_type, width, height); if (!img) { *error = g_error_new(GIV_IMAGE_ERROR, -1, "Failed allocating memory for an image of size %dx%d pixels!", width, height); return NULL; } int png_transforms = PNG_TRANSFORM_PACKING; png_bytep *row_pointers = (png_bytep*)g_new0(gpointer, height); guchar *dst_buf = img->buf.buf; int dst_row_stride = img->row_stride; int row_idx; for (row_idx=0; row_idx<(int)height; row_idx++) row_pointers[row_idx] = dst_buf + dst_row_stride * row_idx; png_read_image(png_ptr, row_pointers); png_timep mod_time; if (png_get_tIME(png_ptr, info_ptr, &mod_time)) { gchar *mod_time_str = g_strdup_printf("%04d-%02d-%02d %02d:%02d:%02d", mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second); giv_image_set_attribute(img, "mod_time", mod_time_str); g_free(mod_time_str); } png_textp png_text; int num_text; if (png_get_text(png_ptr, info_ptr, &png_text, &num_text)) { int i; for (i=0; i<num_text; i++) giv_image_set_attribute(img, png_text[i].key, png_text[i].text); } png_read_end(png_ptr, NULL); g_free(row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); return img; }
/* Shared library entry point */ static GdkPixbuf * gdk_pixbuf__png_image_load (FILE *f, GError **error) { GdkPixbuf * volatile pixbuf = NULL; png_structp png_ptr; png_infop info_ptr; png_textp text_ptr; gint i, ctype; png_uint_32 w, h; png_bytepp volatile rows = NULL; gint num_texts; gchar *key; gchar *value; gchar *icc_profile_base64; const gchar *icc_profile_title; const gchar *icc_profile; png_uint_32 icc_profile_size; guint32 retval; gint compression_type; #ifdef PNG_USER_MEM_SUPPORTED png_ptr = png_create_read_struct_2 (PNG_LIBPNG_VER_STRING, error, png_simple_error_callback, png_simple_warning_callback, NULL, png_malloc_callback, png_free_callback); #else png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, error, png_simple_error_callback, png_simple_warning_callback); #endif if (!png_ptr) return NULL; info_ptr = png_create_info_struct (png_ptr); if (!info_ptr) { png_destroy_read_struct (&png_ptr, NULL, NULL); return NULL; } if (setjmp (png_jmpbuf(png_ptr))) { g_free (rows); if (pixbuf) g_object_unref (pixbuf); png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return NULL; } png_init_io (png_ptr, f); png_read_info (png_ptr, info_ptr); if (!setup_png_transformations(png_ptr, info_ptr, error, &w, &h, &ctype)) { png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return NULL; } pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, ctype & PNG_COLOR_MASK_ALPHA, 8, w, h); if (!pixbuf) { if (error && *error == NULL) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to load PNG file")); } png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return NULL; } rows = g_new (png_bytep, h); for (i = 0; i < h; i++) rows[i] = pixbuf->pixels + i * pixbuf->rowstride; png_read_image (png_ptr, rows); png_read_end (png_ptr, info_ptr); if (png_get_text (png_ptr, info_ptr, &text_ptr, &num_texts)) { for (i = 0; i < num_texts; i++) { png_text_to_pixbuf_option (text_ptr[i], &key, &value); gdk_pixbuf_set_option (pixbuf, key, value); g_free (key); g_free (value); } } #if defined(PNG_cHRM_SUPPORTED) /* Extract embedded ICC profile */ retval = png_get_iCCP (png_ptr, info_ptr, (png_charpp) &icc_profile_title, &compression_type, (png_bytepp) &icc_profile, (png_uint_32*) &icc_profile_size); if (retval != 0) { icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size); gdk_pixbuf_set_option (pixbuf, "icc-profile", icc_profile_base64); g_free (icc_profile_base64); } #endif g_free (rows); png_destroy_read_struct (&png_ptr, &info_ptr, NULL); return pixbuf; }
static BOOL ReadMetadata(png_structp png_ptr, png_infop info_ptr, FIBITMAP *dib) { // XMP keyword const char *g_png_xmp_keyword = "XML:com.adobe.xmp"; FITAG *tag = NULL; png_textp text_ptr = NULL; png_timep mod_time = NULL; int num_text = 0; // iTXt/tEXt/zTXt chuncks if(png_get_text(png_ptr, info_ptr, &text_ptr, &num_text) > 0) { for(int i = 0; i < num_text; i++) { // create a tag tag = FreeImage_CreateTag(); if(!tag) return FALSE; DWORD tag_length = (DWORD) MAX(text_ptr[i].text_length, text_ptr[i].itxt_length); FreeImage_SetTagLength(tag, tag_length); FreeImage_SetTagCount(tag, tag_length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagValue(tag, text_ptr[i].text); if(strcmp(text_ptr[i].key, g_png_xmp_keyword) == 0) { // store the tag as XMP FreeImage_SetTagKey(tag, g_TagLib_XMPFieldName); FreeImage_SetMetadata(FIMD_XMP, dib, FreeImage_GetTagKey(tag), tag); } else { // store the tag as a comment FreeImage_SetTagKey(tag, text_ptr[i].key); FreeImage_SetMetadata(FIMD_COMMENTS, dib, FreeImage_GetTagKey(tag), tag); } // destroy the tag FreeImage_DeleteTag(tag); } } // timestamp chunk if(png_get_tIME(png_ptr, info_ptr, &mod_time)) { char timestamp[32]; // create a tag tag = FreeImage_CreateTag(); if(!tag) return FALSE; // convert as 'yyyy:MM:dd hh:mm:ss' sprintf(timestamp, "%4d:%02d:%02d %2d:%02d:%02d", mod_time->year, mod_time->month, mod_time->day, mod_time->hour, mod_time->minute, mod_time->second); DWORD tag_length = (DWORD)strlen(timestamp) + 1; FreeImage_SetTagLength(tag, tag_length); FreeImage_SetTagCount(tag, tag_length); FreeImage_SetTagType(tag, FIDT_ASCII); FreeImage_SetTagID(tag, TAG_DATETIME); FreeImage_SetTagValue(tag, timestamp); // store the tag as Exif-TIFF FreeImage_SetTagKey(tag, "DateTime"); FreeImage_SetMetadata(FIMD_EXIF_MAIN, dib, FreeImage_GetTagKey(tag), tag); // destroy the tag FreeImage_DeleteTag(tag); } return TRUE; }
/* Called at the start of the progressive load, once we have image info */ static void png_info_callback (png_structp png_read_ptr, png_infop png_info_ptr) { LoadContext* lc; png_uint_32 width, height; png_textp png_text_ptr; int i, num_texts; int color_type; gboolean have_alpha = FALSE; gchar *icc_profile_base64; const gchar *icc_profile_title; const gchar *icc_profile; png_uint_32 icc_profile_size; guint32 retval; gint compression_type; lc = png_get_progressive_ptr(png_read_ptr); if (lc->fatal_error_occurred) return; if (!setup_png_transformations(lc->png_read_ptr, lc->png_info_ptr, lc->error, &width, &height, &color_type)) { lc->fatal_error_occurred = TRUE; return; } /* If we have alpha, set a flag */ if (color_type & PNG_COLOR_MASK_ALPHA) have_alpha = TRUE; if (lc->size_func) { gint w = width; gint h = height; (* lc->size_func) (&w, &h, lc->notify_user_data); if (w == 0 || h == 0) { lc->fatal_error_occurred = TRUE; if (lc->error && *lc->error == NULL) { g_set_error_literal (lc->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_FAILED, _("Transformed PNG has zero width or height.")); } return; } } lc->pixbuf = gdk_pixbuf_new (GDK_COLORSPACE_RGB, have_alpha, 8, width, height); if (lc->pixbuf == NULL) { /* Failed to allocate memory */ lc->fatal_error_occurred = TRUE; if (lc->error && *lc->error == NULL) { g_set_error (lc->error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY, _("Insufficient memory to store a %lu by %lu image; try exiting some applications to reduce memory usage"), (gulong) width, (gulong) height); } return; } /* Extract text chunks and attach them as pixbuf options */ if (png_get_text (png_read_ptr, png_info_ptr, &png_text_ptr, &num_texts)) { for (i = 0; i < num_texts; i++) { gchar *key, *value; if (png_text_to_pixbuf_option (png_text_ptr[i], &key, &value)) { gdk_pixbuf_set_option (lc->pixbuf, key, value); g_free (key); g_free (value); } } } #if defined(PNG_cHRM_SUPPORTED) /* Extract embedded ICC profile */ retval = png_get_iCCP (png_read_ptr, png_info_ptr, (png_charpp) &icc_profile_title, &compression_type, (png_bytepp) &icc_profile, &icc_profile_size); if (retval != 0) { icc_profile_base64 = g_base64_encode ((const guchar *) icc_profile, (gsize)icc_profile_size); gdk_pixbuf_set_option (lc->pixbuf, "icc-profile", icc_profile_base64); g_free (icc_profile_base64); } #endif /* Notify the client that we are ready to go */ if (lc->prepare_func) (* lc->prepare_func) (lc->pixbuf, NULL, lc->notify_user_data); return; }
struct ImBuf *imb_loadpng(unsigned char *mem, int size, int flags) { struct ImBuf *ibuf = 0; png_structp png_ptr; png_infop info_ptr; unsigned char *pixels = 0; png_bytepp row_pointers = 0; png_uint_32 width, height; int bit_depth, color_type; PNGReadStruct ps; unsigned char *from, *to; int i, bytesperpixel; if (imb_is_a_png(mem) == 0) return(0); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { printf("Cannot png_create_read_struct\n"); return 0; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); printf("Cannot png_create_info_struct\n"); return 0; } ps.size = size; ps.data = mem; ps.seek = 0; png_set_read_fn(png_ptr, (void *) &ps, ReadData); if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); if (pixels) MEM_freeN(pixels); if (row_pointers) MEM_freeN(row_pointers); if (ibuf) IMB_freeImBuf(ibuf); return 0; } // png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); if (bit_depth == 16) { png_set_strip_16(png_ptr); bit_depth = 8; } bytesperpixel = png_get_channels(png_ptr, info_ptr); switch(color_type) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: break; case PNG_COLOR_TYPE_PALETTE: png_set_palette_to_rgb(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { bytesperpixel = 4; } else { bytesperpixel = 3; } break; case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (bit_depth < 8) { png_set_expand(png_ptr); bit_depth = 8; } break; default: printf("PNG format not supported\n"); longjmp(png_jmpbuf(png_ptr), 1); } ibuf = IMB_allocImBuf(width, height, 8 * bytesperpixel, 0, 0); if (ibuf) { ibuf->ftype = PNG; } else { printf("Couldn't allocate memory for PNG image\n"); } if (ibuf && ((flags & IB_test) == 0)) { imb_addrectImBuf(ibuf); pixels = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned char), "pixels"); if (pixels == NULL) { printf("Cannot allocate pixels array\n"); longjmp(png_jmpbuf(png_ptr), 1); } // allocate memory for an array of row-pointers row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers"); if (row_pointers == NULL) { printf("Cannot allocate row-pointers array\n"); longjmp(png_jmpbuf(png_ptr), 1); } // set the individual row-pointers to point at the correct offsets for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y-1-i] = (png_bytep) ((unsigned char *)pixels + (i * ibuf->x) * bytesperpixel * sizeof(unsigned char)); } png_read_image(png_ptr, row_pointers); // copy image data to = (unsigned char *) ibuf->rect; from = pixels; switch (bytesperpixel) { case 4: for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to[3] = from[3]; to += 4; from += 4; } break; case 3: for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to[3] = 0xff; to += 4; from += 3; } break; case 2: for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = to[1] = to[2] = from[0]; to[3] = from[1]; to += 4; from += 2; } break; case 1: for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = to[1] = to[2] = from[0]; to[3] = 0xff; to += 4; from++; } break; } if (flags & IB_imginfo) { png_text* text_chunks; int count = png_get_text(png_ptr, info_ptr, &text_chunks, NULL); for(i = 0; i < count; i++) { IMB_imginfo_add_field(ibuf, text_chunks[i].key, text_chunks[i].text); ibuf->flags |= IB_imginfo; } } png_read_end(png_ptr, info_ptr); } // clean up MEM_freeN(pixels); MEM_freeN(row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return(ibuf); }
int main( int argc, char *argv[] ) { int f, rowbytes; char buf[256]; static FILE *fpout; /* "static" prevents setjmp corruption */ png_structp write_ptr; png_infop write_info_ptr, end_info_ptr; png_bytep row_buf, here; png_uint_32 y; png_textp text_ptr, new_text_ptr; int num_text; int interlace_type, compression_type, filter_type, bit_depth, color_type; int it, ct, ft, bd, clrt; png_uint_32 width, height, w, h; int duration; if( argc < 4 ) { printf( "makeanim v0.2\nusage: makeanim <duration in milliseconds> <input files ...> <output file>\n" ); printf( "example: makeanim 1500 a00.png a01.png a02.png a03.png a04.png a.anim\n" ); return 1; } duration = atoi( argv[1] ); if( duration < 1 ) { printf( "duration is incorrect\n" ); return 1; } numfiles = argc - 3; input = (struct inputstruct *)malloc( sizeof( struct inputstruct ) * numfiles ); if( !input ) return 1; for( f = 0; f < numfiles; f++ ) { input[f].name = argv[f + 2]; printf( "opening file %d, \"%s\"\n", f, input[f].name ); /* open the file handle */ input[f].file = fopen( input[f].name, "rb" ); if( input[f].file == NULL ) { printf( "fopen() failed\n" ); return 1; } /* check if it's PNG */ if( fread( buf, 1, 8, input[f].file ) != 8 ) { printf( "fread() failed for file \"%s\"\n", input[f].name ); return 1; } if( png_sig_cmp( buf, (png_size_t)0, 8 ) ) { printf( "not a PNG file\n" ); return 1; } fseek( input[f].file, 0, SEEK_SET ); /* allocate read structure */ input[f].read_ptr = png_create_read_struct( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL ); if( input[f].read_ptr == NULL ) { printf( "png_create_read_struct() failed\n" ); return 1; } /* allocate read info structure */ input[f].read_info_ptr = png_create_info_struct( input[f].read_ptr ); if( input[f].read_info_ptr == NULL ) { printf( "png_create_info_struct() failed\n" ); return 1; } /* set error handler code */ if( setjmp( input[f].read_ptr->jmpbuf ) ) { printf( "libpng read error\n" ); return 1; } /* initialize stream */ png_init_io( input[f].read_ptr, input[f].file ); png_set_read_status_fn( input[f].read_ptr, NULL ); /* read png info struct */ png_read_info( input[f].read_ptr, input[f].read_info_ptr ); /* get the info */ if( !png_get_IHDR( input[f].read_ptr, input[f].read_info_ptr, &w, &h, &bd, &clrt, &it, &ct, &ft ) ) { printf( "png_get_IHDR() failed\n" ); return 1; } /* save the info of the first frame */ if( f == 0 ) { width = w; height = h; bit_depth = bd; color_type = clrt; interlace_type = it; compression_type = ct; filter_type = ft; } /* compare all other frames to first frame */ else if( (w != width) || (h != height) || (bd != bit_depth) || (clrt != color_type) || (it != interlace_type) || (ct != compression_type) || (ft != filter_type) ) { if( w != width ) printf( "width is different\n" ); if( h != height ) printf( "height is different\n" ); if( bd != bit_depth ) printf( "bit depth is different\n" ); if( clrt != color_type ) printf( "color type is different\n" ); if( it != interlace_type ) printf( "interlace type is different\n" ); if( ct != compression_type ) printf( "compression type is different\n" ); if( ft != filter_type ) printf( "filter type is different\n" ); return 1; } } row_buf = (png_bytep)NULL; /* open output file */ printf( "opening file \"%s\"\n", argv[numfiles + 2] ); fpout = fopen( argv[numfiles + 2], "wb" ); if( fpout == NULL ) { printf( "fopen() failed\n" ); return 1; } /* allocate write structure */ write_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL ); /* allocate info structures */ write_info_ptr = png_create_info_struct( write_ptr ); end_info_ptr = png_create_info_struct( write_ptr ); /* error handling */ if( setjmp( write_ptr->jmpbuf ) ) { printf( "libpng write error\n" ); return 1; } /* initialize output stream */ png_init_io( write_ptr, fpout ); png_set_write_status_fn( write_ptr, NULL ); /* set info */ png_set_IHDR( write_ptr, write_info_ptr, width * numfiles, height, bit_depth, color_type, PNG_INTERLACE_NONE, compression_type, filter_type); /* image characteristics */ { png_color_16p background; double white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y; double gamma; int intent; png_uint_16p hist; png_uint_32 offset_x, offset_y; int unit_type; png_charp purpose, units; png_charpp params; png_int_32 X0, X1; int type, nparams; png_uint_32 res_x, res_y; png_colorp palette; int num_palette; png_color_8p sig_bit; png_bytep trans; int num_trans; png_color_16p trans_values; /* background color */ if( png_get_bKGD( input[0].read_ptr, input[0].read_info_ptr, &background ) ) { png_set_bKGD( write_ptr, write_info_ptr, background ); } if( png_get_cHRM( input[0].read_ptr, input[0].read_info_ptr, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y ) ) { png_set_cHRM( write_ptr, write_info_ptr, white_x, white_y, red_x, red_y, green_x, green_y, blue_x, blue_y ); } /* gamma */ if( png_get_gAMA( input[0].read_ptr, input[0].read_info_ptr, &gamma ) ) { png_set_gAMA( write_ptr, write_info_ptr, gamma ); } /* rendering intent */ if( png_get_sRGB( input[0].read_ptr, input[0].read_info_ptr, &intent ) ) { png_set_sRGB( write_ptr, write_info_ptr, intent ); } /* Histogram */ if( png_get_hIST( input[0].read_ptr, input[0].read_info_ptr, &hist ) ) { png_set_hIST( write_ptr, write_info_ptr, hist ); } /* offsets */ if( png_get_oFFs( input[0].read_ptr, input[0].read_info_ptr, &offset_x, &offset_y, &unit_type ) ) { png_set_oFFs( write_ptr, write_info_ptr, offset_x, offset_y, unit_type ); } if( png_get_pCAL( input[0].read_ptr, input[0].read_info_ptr, &purpose, &X0, &X1, &type, &nparams, &units, ¶ms ) ) { png_set_pCAL( write_ptr, write_info_ptr, purpose, X0, X1, type, nparams, units, params ); } /* pixel density */ if( png_get_pHYs( input[0].read_ptr, input[0].read_info_ptr, &res_x, &res_y, &unit_type ) ) { png_set_pHYs( write_ptr, write_info_ptr, res_x, res_y, unit_type ); } /* text chunks */ /* if( png_get_text( input[0].read_ptr, input[0].read_info_ptr, &text_ptr, &num_text ) > 0 ) { printf( "Handling %d tEXt/zTXt chunks\n", num_text ); png_set_text( write_ptr, write_info_ptr, text_ptr, num_text ); } */ /* palette */ if( png_get_PLTE( input[0].read_ptr, input[0].read_info_ptr, &palette, &num_palette ) ) { png_set_PLTE( write_ptr, write_info_ptr, palette, num_palette ); } /* significant bits */ if( png_get_sBIT( input[0].read_ptr, input[0].read_info_ptr, &sig_bit ) ) { png_set_sBIT( write_ptr, write_info_ptr, sig_bit ); } /* transparency */ if( png_get_tRNS( input[0].read_ptr, input[0].read_info_ptr, &trans, &num_trans, &trans_values ) ) { png_set_tRNS( write_ptr, write_info_ptr, trans, num_trans, trans_values ); } } /* text chunks */ num_text = 0; if( !png_get_text( input[0].read_ptr, input[0].read_info_ptr, &text_ptr, &num_text ) ) num_text = 0; new_text_ptr = (struct png_text_struct *)malloc( sizeof( struct png_text_struct ) * num_text + 1 ); if( !new_text_ptr ) { printf( "malloc() failed\n" ); return 1; } memcpy( new_text_ptr, text_ptr, sizeof( struct png_text_struct ) * num_text ); snprintf( buf, 255, "SDL_anim %d %d %d", duration, width, numfiles ); buf[255] = 0; new_text_ptr[num_text].compression = PNG_TEXT_COMPRESSION_NONE; new_text_ptr[num_text].key = "format"; new_text_ptr[num_text].text = buf; new_text_ptr[num_text].text_length = strlen( buf ); num_text++; png_set_text( write_ptr, write_info_ptr, new_text_ptr, num_text ); /* write info */ png_write_info( write_ptr, write_info_ptr ); /* allocate buffer */ rowbytes = png_get_rowbytes( input[0].read_ptr, input[0].read_info_ptr ); row_buf = (png_bytep)png_malloc( write_ptr, rowbytes * numfiles ); if( row_buf == NULL ) { printf( "png_malloc() failed\n" ); return 1; } /* copy raw data */ for( y = 0; y < height; y++ ) { /* grab a scanline from each file */ here = row_buf; for( f = 0; f < numfiles; f++ ) { png_read_rows( input[f].read_ptr, (png_bytepp)&here, (png_bytepp)NULL, 1 ); here += rowbytes; } /* write the long scanline */ png_write_rows( write_ptr, (png_bytepp)&row_buf, 1 ); } /* end io */ for( f = 0; f < numfiles; f++ ) png_read_end( input[f].read_ptr, end_info_ptr ); png_write_end( write_ptr, end_info_ptr ); /* cleanup */ png_free( write_ptr, row_buf ); for( f = 0; f < numfiles; f++ ) { png_destroy_read_struct( &input[f].read_ptr, &input[f].read_info_ptr, &end_info_ptr); fclose( input[f].file ); } png_destroy_write_struct( &write_ptr, &write_info_ptr ); fclose( fpout ); return 0; }
ImBuf *imb_loadpng(unsigned char *mem, size_t size, int flags, char colorspace[IM_MAX_SPACE]) { struct ImBuf *ibuf = NULL; png_structp png_ptr; png_infop info_ptr; unsigned char *pixels = NULL; unsigned short *pixels16 = NULL; png_bytepp row_pointers = NULL; png_uint_32 width, height; int bit_depth, color_type; PNGReadStruct ps; unsigned char *from, *to; unsigned short *from16; float *to_float; int i, bytesperpixel; if (imb_is_a_png(mem) == 0) return(NULL); /* both 8 and 16 bit PNGs are default to standard byte colorspace */ colorspace_set_default_role(colorspace, IM_MAX_SPACE, COLOR_ROLE_DEFAULT_BYTE); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { printf("Cannot png_create_read_struct\n"); return NULL; } info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL); printf("Cannot png_create_info_struct\n"); return NULL; } ps.size = size; /* XXX, 4gig limit! */ ps.data = mem; ps.seek = 0; png_set_read_fn(png_ptr, (void *) &ps, ReadData); if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); if (row_pointers) MEM_freeN(row_pointers); if (ibuf) IMB_freeImBuf(ibuf); return NULL; } // png_set_sig_bytes(png_ptr, 8); png_read_info(png_ptr, info_ptr); png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth, &color_type, NULL, NULL, NULL); bytesperpixel = png_get_channels(png_ptr, info_ptr); switch (color_type) { case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: break; case PNG_COLOR_TYPE_PALETTE: png_set_palette_to_rgb(png_ptr); if (png_get_valid(png_ptr, info_ptr, PNG_INFO_tRNS)) { bytesperpixel = 4; } else { bytesperpixel = 3; } break; case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (bit_depth < 8) { png_set_expand(png_ptr); bit_depth = 8; } break; default: printf("PNG format not supported\n"); longjmp(png_jmpbuf(png_ptr), 1); } ibuf = IMB_allocImBuf(width, height, 8 * bytesperpixel, 0); if (ibuf) { ibuf->ftype = PNG; if (bit_depth == 16) ibuf->ftype |= PNG_16BIT; if (png_get_valid(png_ptr, info_ptr, PNG_INFO_pHYs)) { int unit_type; png_uint_32 xres, yres; if (png_get_pHYs(png_ptr, info_ptr, &xres, &yres, &unit_type)) if (unit_type == PNG_RESOLUTION_METER) { ibuf->ppm[0] = xres; ibuf->ppm[1] = yres; } } } else { printf("Couldn't allocate memory for PNG image\n"); } if (ibuf && ((flags & IB_test) == 0)) { if (bit_depth == 16) { imb_addrectfloatImBuf(ibuf); png_set_swap(png_ptr); pixels16 = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(png_uint_16), "pixels"); if (pixels16 == NULL) { printf("Cannot allocate pixels array\n"); longjmp(png_jmpbuf(png_ptr), 1); } /* allocate memory for an array of row-pointers */ row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_uint_16p), "row_pointers"); if (row_pointers == NULL) { printf("Cannot allocate row-pointers array\n"); longjmp(png_jmpbuf(png_ptr), 1); } /* set the individual row-pointers to point at the correct offsets */ for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y - 1 - i] = (png_bytep) ((png_uint_16 *)pixels16 + (i * ibuf->x) * bytesperpixel); } png_read_image(png_ptr, row_pointers); /* copy image data */ to_float = ibuf->rect_float; from16 = pixels16; switch (bytesperpixel) { case 4: for (i = ibuf->x * ibuf->y; i > 0; i--) { to_float[0] = from16[0] / 65535.0; to_float[1] = from16[1] / 65535.0; to_float[2] = from16[2] / 65535.0; to_float[3] = from16[3] / 65535.0; to_float += 4; from16 += 4; } break; case 3: for (i = ibuf->x * ibuf->y; i > 0; i--) { to_float[0] = from16[0] / 65535.0; to_float[1] = from16[1] / 65535.0; to_float[2] = from16[2] / 65535.0; to_float[3] = 1.0; to_float += 4; from16 += 3; } break; case 2: for (i = ibuf->x * ibuf->y; i > 0; i--) { to_float[0] = to_float[1] = to_float[2] = from16[0] / 65535.0; to_float[3] = from16[1] / 65535.0; to_float += 4; from16 += 2; } break; case 1: for (i = ibuf->x * ibuf->y; i > 0; i--) { to_float[0] = to_float[1] = to_float[2] = from16[0] / 65535.0; to_float[3] = 1.0; to_float += 4; from16++; } break; } } else { imb_addrectImBuf(ibuf); pixels = MEM_mallocN(ibuf->x * ibuf->y * bytesperpixel * sizeof(unsigned char), "pixels"); if (pixels == NULL) { printf("Cannot allocate pixels array\n"); longjmp(png_jmpbuf(png_ptr), 1); } /* allocate memory for an array of row-pointers */ row_pointers = (png_bytepp) MEM_mallocN(ibuf->y * sizeof(png_bytep), "row_pointers"); if (row_pointers == NULL) { printf("Cannot allocate row-pointers array\n"); longjmp(png_jmpbuf(png_ptr), 1); } /* set the individual row-pointers to point at the correct offsets */ for (i = 0; i < ibuf->y; i++) { row_pointers[ibuf->y - 1 - i] = (png_bytep) ((unsigned char *)pixels + (i * ibuf->x) * bytesperpixel * sizeof(unsigned char)); } png_read_image(png_ptr, row_pointers); /* copy image data */ to = (unsigned char *) ibuf->rect; from = pixels; switch (bytesperpixel) { case 4: for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to[3] = from[3]; to += 4; from += 4; } break; case 3: for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = from[0]; to[1] = from[1]; to[2] = from[2]; to[3] = 0xff; to += 4; from += 3; } break; case 2: for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = to[1] = to[2] = from[0]; to[3] = from[1]; to += 4; from += 2; } break; case 1: for (i = ibuf->x * ibuf->y; i > 0; i--) { to[0] = to[1] = to[2] = from[0]; to[3] = 0xff; to += 4; from++; } break; } } if (flags & IB_metadata) { png_text *text_chunks; int count = png_get_text(png_ptr, info_ptr, &text_chunks, NULL); for (i = 0; i < count; i++) { IMB_metadata_add_field(ibuf, text_chunks[i].key, text_chunks[i].text); ibuf->flags |= IB_metadata; } } png_read_end(png_ptr, info_ptr); } /* clean up */ if (pixels) MEM_freeN(pixels); if (pixels16) MEM_freeN(pixels16); if (row_pointers) MEM_freeN(row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL); return(ibuf); }
int png_include_image (pdf_ximage *ximage, FILE *png_file) { pdf_obj *stream; pdf_obj *stream_dict; pdf_obj *colorspace, *mask, *intent; png_bytep stream_data_ptr; int trans_type; ximage_info info; /* Libpng stuff */ png_structp png_ptr; png_infop png_info_ptr; png_byte bpc, color_type; png_uint_32 width, height, rowbytes; pdf_ximage_init_image_info(&info); stream = NULL; stream_dict = NULL; colorspace = mask = intent = NULL; rewind (png_file); png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, warn); if (png_ptr == NULL || (png_info_ptr = png_create_info_struct (png_ptr)) == NULL) { WARN("%s: Creating Libpng read/info struct failed.", PNG_DEBUG_STR); if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL); return -1; } #if PNG_LIBPNG_VER >= 10603 /* ignore possibly incorrect CMF bytes */ png_set_option(png_ptr, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON); #endif /* Inititializing file IO. */ png_init_io (png_ptr, png_file); /* Read PNG info-header and get some info. */ png_read_info(png_ptr, png_info_ptr); color_type = png_get_color_type (png_ptr, png_info_ptr); width = png_get_image_width (png_ptr, png_info_ptr); height = png_get_image_height(png_ptr, png_info_ptr); bpc = png_get_bit_depth (png_ptr, png_info_ptr); /* Ask libpng to convert down to 8-bpc. */ if (bpc > 8) { if (pdf_get_version() < 5) { WARN("%s: 16-bpc PNG requires PDF version 1.5.", PNG_DEBUG_STR); png_set_strip_16(png_ptr); bpc = 8; } } /* Ask libpng to gamma-correct. * It is wrong to assume screen gamma value 2.2 but... * We do gamma correction here only when uncalibrated color space is used. */ if (!png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP) && !png_get_valid(png_ptr, png_info_ptr, PNG_INFO_sRGB) && !png_get_valid(png_ptr, png_info_ptr, PNG_INFO_cHRM) && png_get_valid(png_ptr, png_info_ptr, PNG_INFO_gAMA)) { double G = 1.0; png_get_gAMA (png_ptr, png_info_ptr, &G); png_set_gamma(png_ptr, 2.2, G); } trans_type = check_transparency(png_ptr, png_info_ptr); /* check_transparency() does not do updata_info() */ png_read_update_info(png_ptr, png_info_ptr); rowbytes = png_get_rowbytes(png_ptr, png_info_ptr); /* Values listed below will not be modified in the remaining process. */ info.width = width; info.height = height; info.bits_per_component = bpc; if (compat_mode) info.xdensity = info.ydensity = 72.0 / 100.0; else { png_uint_32 xppm = png_get_x_pixels_per_meter(png_ptr, png_info_ptr); png_uint_32 yppm = png_get_y_pixels_per_meter(png_ptr, png_info_ptr); if (xppm > 0) info.xdensity = 72.0 / 0.0254 / xppm; if (yppm > 0) info.ydensity = 72.0 / 0.0254 / yppm; } stream = pdf_new_stream (STREAM_COMPRESS); stream_dict = pdf_stream_dict(stream); stream_data_ptr = (png_bytep) NEW(rowbytes*height, png_byte); read_image_data(png_ptr, stream_data_ptr, height, rowbytes); /* Non-NULL intent means there is valid sRGB chunk. */ intent = get_rendering_intent(png_ptr, png_info_ptr); if (intent) pdf_add_dict(stream_dict, pdf_new_name("Intent"), intent); switch (color_type) { case PNG_COLOR_TYPE_PALETTE: colorspace = create_cspace_Indexed(png_ptr, png_info_ptr); switch (trans_type) { case PDF_TRANS_TYPE_BINARY: /* Color-key masking */ mask = create_ckey_mask(png_ptr, png_info_ptr); break; case PDF_TRANS_TYPE_ALPHA: /* Soft mask */ mask = create_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, width, height); break; default: /* Nothing to be done here. * No tRNS chunk or image already composited with background color. */ break; } info.num_components = 1; break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP)) colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr); else if (intent) { colorspace = create_cspace_sRGB(png_ptr, png_info_ptr); } else { colorspace = create_cspace_CalRGB(png_ptr, png_info_ptr); } if (!colorspace) colorspace = pdf_new_name("DeviceRGB"); switch (trans_type) { case PDF_TRANS_TYPE_BINARY: mask = create_ckey_mask(png_ptr, png_info_ptr); break; /* rowbytes changes 4 to 3 at here */ case PDF_TRANS_TYPE_ALPHA: mask = strip_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, &rowbytes, width, height); break; default: mask = NULL; } info.num_components = 3; break; case PNG_COLOR_TYPE_GRAY: case PNG_COLOR_TYPE_GRAY_ALPHA: if (png_get_valid(png_ptr, png_info_ptr, PNG_INFO_iCCP)) colorspace = create_cspace_ICCBased(png_ptr, png_info_ptr); else if (intent) { colorspace = create_cspace_sRGB(png_ptr, png_info_ptr); } else { colorspace = create_cspace_CalGray(png_ptr, png_info_ptr); } if (!colorspace) colorspace = pdf_new_name("DeviceGray"); switch (trans_type) { case PDF_TRANS_TYPE_BINARY: mask = create_ckey_mask(png_ptr, png_info_ptr); break; case PDF_TRANS_TYPE_ALPHA: mask = strip_soft_mask(png_ptr, png_info_ptr, stream_data_ptr, &rowbytes, width, height); break; default: mask = NULL; } info.num_components = 1; break; default: WARN("%s: Unknown PNG colortype %d.", PNG_DEBUG_STR, color_type); } pdf_add_dict(stream_dict, pdf_new_name("ColorSpace"), colorspace); pdf_add_stream(stream, stream_data_ptr, rowbytes*height); RELEASE(stream_data_ptr); if (mask) { if (trans_type == PDF_TRANS_TYPE_BINARY) pdf_add_dict(stream_dict, pdf_new_name("Mask"), mask); else if (trans_type == PDF_TRANS_TYPE_ALPHA) { if (info.bits_per_component >= 8 && info.width > 64) { pdf_stream_set_predictor(mask, 2, info.width, info.bits_per_component, 1); } pdf_add_dict(stream_dict, pdf_new_name("SMask"), pdf_ref_obj(mask)); pdf_release_obj(mask); } else { WARN("%s: Unknown transparency type...???", PNG_DEBUG_STR); pdf_release_obj(mask); } } /* Finally read XMP Metadata * See, XMP Specification Part 3, Storage in Files * http://www.adobe.com/jp/devnet/xmp.html * * We require libpng version >= 1.6.14 since prior versions * of libpng had a bug that incorrectly treat the compression * flag of iTxt chunks. */ #if PNG_LIBPNG_VER >= 10614 if (pdf_get_version() >= 4) { png_textp text_ptr; pdf_obj *XMP_stream, *XMP_stream_dict; int i, num_text; int have_XMP = 0; num_text = png_get_text(png_ptr, png_info_ptr, &text_ptr, NULL); for (i = 0; i < num_text; i++) { if (!memcmp(text_ptr[i].key, "XML:com.adobe.xmp", 17)) { /* XMP found */ if (text_ptr[i].compression != PNG_ITXT_COMPRESSION_NONE || text_ptr[i].itxt_length == 0) WARN("%s: Invalid value(s) in iTXt chunk for XMP Metadata.", PNG_DEBUG_STR); else if (have_XMP) WARN("%s: Multiple XMP Metadata. Don't know how to treat it.", PNG_DEBUG_STR); else { /* We compress XMP metadata for included images here. * It is not recommended to compress XMP metadata for PDF documents but * we compress XMP metadata for included images here to avoid confusing * application programs that only want PDF document global XMP metadata * and scan for that. */ XMP_stream = pdf_new_stream(STREAM_COMPRESS); XMP_stream_dict = pdf_stream_dict(XMP_stream); pdf_add_dict(XMP_stream_dict, pdf_new_name("Type"), pdf_new_name("Metadata")); pdf_add_dict(XMP_stream_dict, pdf_new_name("Subtype"), pdf_new_name("XML")); pdf_add_stream(XMP_stream, text_ptr[i].text, text_ptr[i].itxt_length); pdf_add_dict(stream_dict, pdf_new_name("Metadata"), pdf_ref_obj(XMP_stream)); pdf_release_obj(XMP_stream); have_XMP = 1; } } } } #endif /* PNG_LIBPNG_VER */ png_read_end(png_ptr, NULL); /* Cleanup */ if (png_info_ptr) png_destroy_info_struct(png_ptr, &png_info_ptr); if (png_ptr) png_destroy_read_struct(&png_ptr, NULL, NULL); if (color_type != PNG_COLOR_TYPE_PALETTE && info.bits_per_component >= 8 && info.height > 64) { pdf_stream_set_predictor(stream, 15, info.width, info.bits_per_component, info.num_components); } pdf_ximage_set_image(ximage, &info, stream); return 0; }
s32 pngDecodeData(PPUThread& ppu, PHandle handle, PStream stream, vm::ptr<u8> data, PDataControlParam data_control_param, PDataOutInfo data_out_info, PCbControlDisp cb_control_disp = vm::null, PDispParam disp_param = vm::null) { if (cb_control_disp || disp_param) { throw EXCEPTION("Partial image decoding is not supported"); } // Indicate, that the PNG decoding is stopped/failed. This is incase, we return an error code in the middle of decoding data_out_info->status = CELL_PNGDEC_DEC_STATUS_STOP; // Possibilities for decoding in different sizes aren't tested, so if anyone finds any of these cases, we'll know about it. if (stream->info.imageWidth != stream->out_param.outputWidth) { throw EXCEPTION("Image width doesn't match output width! (%d/%d)", stream->out_param.outputWidth, stream->info.imageWidth); } if (stream->info.imageHeight != stream->out_param.outputHeight) { throw EXCEPTION("Image width doesn't match output height! (%d/%d)", stream->out_param.outputHeight, stream->info.imageHeight); } // Get the amount of output bytes per line const u32 bytes_per_line = data_control_param->outputBytesPerLine; // Whether to recaculate bytes per row bool recalculate_bytes_per_row = false; // Check if the game is expecting the number of bytes per line to be lower, than the actual bytes per line on the image. (Arkedo Pixel for example) // In such case we strip the bit depth to be lower. if ((bytes_per_line < stream->out_param.outputWidthByte) && stream->out_param.outputBitDepth != 8) { // Check if the packing is really 1 byte per 1 pixel if (stream->packing != CELL_PNGDEC_1BYTE_PER_1PIXEL) { throw EXCEPTION("Unexpected packing value! (%d)", stream->packing); } // Scale 16 bit depth down to 8 bit depth. PS3 uses png_set_strip_16, since png_set_scale_16 wasn't available back then. png_set_strip_16(stream->png_ptr); recalculate_bytes_per_row = true; } // Check if the outputWidthByte is smaller than the intended output length of a line. For example an image might be in RGB, but we need to output 4 components, so we need to perform alpha padding. else if (stream->out_param.outputWidthByte < (stream->out_param.outputWidth * stream->out_param.outputComponents)) { // If fixed alpha is not specified in such a case, the default value for the alpha is 0xFF (255) if (!stream->fixed_alpha) { stream->fixed_alpha_colour = 0xFF; } // We need to fill alpha (before or after, depending on the output colour format) using the fixed alpha value passed by the game. png_set_add_alpha(stream->png_ptr, stream->fixed_alpha_colour, stream->out_param.outputColorSpace == CELL_PNGDEC_RGBA ? PNG_FILLER_AFTER : PNG_FILLER_BEFORE); recalculate_bytes_per_row = true; } // We decode as RGBA, so we need to swap the alpha else if (stream->out_param.outputColorSpace == CELL_PNGDEC_ARGB) { // Swap the alpha channel for the ARGB output format, if the padding isn't needed png_set_swap_alpha(stream->png_ptr); } // Sometimes games pass in a RBG/RGBA image and want it as grayscale else if ((stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE_ALPHA || stream->out_param.outputColorSpace == CELL_PNGDEC_GRAYSCALE) && (stream->info.colorSpace == CELL_PNGDEC_RGB || stream->info.colorSpace == CELL_PNGDEC_RGBA)) { // Tell libpng to convert it to grayscale png_set_rgb_to_gray(stream->png_ptr, PNG_ERROR_ACTION_NONE, PNG_RGB_TO_GRAY_DEFAULT, PNG_RGB_TO_GRAY_DEFAULT); recalculate_bytes_per_row = true; } if (recalculate_bytes_per_row) { // Update the info structure png_read_update_info(stream->png_ptr, stream->info_ptr); // Recalculate the bytes per row stream->out_param.outputWidthByte = png_get_rowbytes(stream->png_ptr, stream->info_ptr); } // Calculate the image size u32 image_size = stream->out_param.outputWidthByte * stream->out_param.outputHeight; // Buffer for storing the image std::vector<u8> png(image_size); // Make an unique pointer for the row pointers std::vector<u8*> row_pointers(stream->out_param.outputHeight); // Allocate memory for rows for (u32 y = 0; y < stream->out_param.outputHeight; y++) { row_pointers[y] = &png[y * stream->out_param.outputWidthByte]; } // Decode the image png_read_image(stream->png_ptr, row_pointers.data()); // Check if the image needs to be flipped const bool flip = stream->out_param.outputMode == CELL_PNGDEC_BOTTOM_TO_TOP; // Copy the result to the output buffer switch (stream->out_param.outputColorSpace) { case CELL_PNGDEC_RGB: case CELL_PNGDEC_RGBA: case CELL_PNGDEC_ARGB: case CELL_PNGDEC_GRAYSCALE_ALPHA: { // Check if we need to flip the image or need to leave empty bytes at the end of a line if ((bytes_per_line > stream->out_param.outputWidthByte) || flip) { // Get how many bytes per line we need to output - bytesPerLine is total amount of bytes per line, rest is unused and the game can do as it pleases. const u32 line_size = std::min(bytes_per_line, stream->out_param.outputWidth * 4); // If the game wants more bytes per line to be output, than the image has, then we simply copy what we have for each line, // and continue on the next line, thus leaving empty bytes at the end of the line. for (u32 i = 0; i < stream->out_param.outputHeight; i++) { const u32 dst = i * bytes_per_line; const u32 src = stream->out_param.outputWidth * 4 * (flip ? stream->out_param.outputHeight - i - 1 : i); memcpy(&data[dst], &png[src], line_size); } } else { // We can simply copy the output to the data pointer specified by the game, since we already do alpha channel transformations in libpng, if needed memcpy(data.get_ptr(), png.data(), image_size); } break; } default: throw EXCEPTION("Unsupported color space (%d)", stream->out_param.outputColorSpace); } // Get the number of iTXt, tEXt and zTXt chunks s32 text_chunks = 0; png_get_text(stream->png_ptr, stream->info_ptr, nullptr, &text_chunks); // Set the chunk information and the previously obtained number of text chunks data_out_info->numText = (u32)text_chunks; data_out_info->chunkInformation = pngDecGetChunkInformation(stream, true); data_out_info->numUnknownChunk = 0; // TODO: Get this somehow. Does anything even use or need this? // Indicate that the decoding succeeded data_out_info->status = CELL_PNGDEC_DEC_STATUS_FINISH; return CELL_OK; }
static GpStatus gdip_load_png_properties (png_structp png_ptr, png_infop info_ptr, png_infop end_ptr, BitmapData *bitmap_data) { #if defined(PNG_INCH_CONVERSIONS) && defined(PNG_FLOATING_POINT_SUPPORTED) bitmap_data->image_flags |= ImageFlagsHasRealDPI; bitmap_data->dpi_horz = png_get_x_pixels_per_inch(png_ptr, info_ptr); bitmap_data->dpi_vert = png_get_y_pixels_per_inch(png_ptr, info_ptr); #elif defined(PNG_pHYs_SUPPORTED) if ((info_ptr->valid & PNG_INFO_pHYs) && (info_ptr->phys_unit_type == PNG_RESOLUTION_METER)) { bitmap_data->image_flags |= ImageFlagsHasRealDPI; bitmap_data->dpi_horz = info_ptr->x_pixels_per_unit * 0.0254; bitmap_data->dpi_vert = info_ptr->y_pixels_per_unit * 0.0254; } #endif /* default to screen resolution (if nothing was provided or available) */ if (bitmap_data->dpi_horz == 0 || bitmap_data->dpi_vert == 0) { bitmap_data->dpi_horz = bitmap_data->dpi_vert = gdip_get_display_dpi (); } #if defined(PNG_iCCP_SUPPORTED) { png_charp name; png_charp profile; png_uint_32 proflen; int compression_type; if (png_get_iCCP(png_ptr, info_ptr, &name, &compression_type, &profile, &proflen)) { gdip_bitmapdata_property_add_ASCII(bitmap_data, PropertyTagICCProfileDescriptor, (BYTE*)name); gdip_bitmapdata_property_add_byte(bitmap_data, PropertyTagICCProfile, (BYTE)compression_type); } } #endif #if defined(PNG_gAMA_SUPPORTED) { double gamma; if (png_get_gAMA(png_ptr, info_ptr, &gamma)) { gdip_bitmapdata_property_add_rational(bitmap_data, PropertyTagGamma, 100000, gamma * 100000); } } #endif #if defined(PNG_cHRM_SUPPORTED) { double white_x; double white_y; double red_x; double red_y; double green_x; double green_y; double blue_x; double blue_y; if (png_get_cHRM(png_ptr, info_ptr, &white_x, &white_y, &red_x, &red_y, &green_x, &green_y, &blue_x, &blue_y)) { BYTE *buffer; guint32 *ptr; buffer = GdipAlloc(6 * (sizeof(png_uint_32) + sizeof(png_uint_32))); if (buffer != NULL) { ptr = (guint32 *)buffer; ptr[0] = (guint32)(red_x * 100000); ptr[1] = 1000000; ptr[2] = (guint32)(red_y * 100000); ptr[3] = 100000; ptr[4] = (guint32)(green_x * 100000); ptr[5] = 1000000; ptr[6] = (guint32)(green_y * 100000); ptr[7] = 100000; ptr[8] = (guint32)(blue_x * 100000); ptr[9] = 100000; ptr[10] = (guint32)(blue_y * 100000); ptr[11] = 100000; gdip_bitmapdata_property_add (bitmap_data, PropertyTagPrimaryChromaticities, 6 * (sizeof(guint32) + sizeof(guint32)), PropertyTagTypeRational, buffer); ptr[0] = (guint32)(white_x * 100000); ptr[1] = 1000000; ptr[2] = (guint32)(white_y * 100000); ptr[3] = 100000; gdip_bitmapdata_property_add (bitmap_data, PropertyTagWhitePoint, 2 * (sizeof(guint32) + sizeof(guint32)), PropertyTagTypeRational, buffer); GdipFree(buffer); } } } #endif #if defined(PNG_pHYs_SUPPORTED) { int unit_type; png_uint_32 res_x; png_uint_32 res_y; if (png_get_pHYs(png_ptr, info_ptr, &res_x, &res_y, &unit_type)) { gdip_bitmapdata_property_add_byte(bitmap_data, PropertyTagPixelUnit, (BYTE)unit_type); gdip_bitmapdata_property_add_long(bitmap_data, PropertyTagPixelPerUnitX, res_x); gdip_bitmapdata_property_add_long(bitmap_data, PropertyTagPixelPerUnitY, res_y); } } #endif #if defined(PNG_TEXT_SUPPORTED) { int num_text; png_textp text_ptr; if (png_get_text(png_ptr, info_ptr, &text_ptr, &num_text)) { if (num_text > 0) { gdip_bitmapdata_property_add_ASCII(bitmap_data, PropertyTagExifUserComment, (BYTE*)text_ptr[0].text); } } } #endif return Ok; }
// Looks for metadata at both the beginning and end of the PNG file, giving // preference to the head. // Returns true on success. The caller must use MetadataFree() on 'metadata' in // all cases. static int ExtractMetadataFromPNG(png_structp png, png_infop const head_info, png_infop const end_info, Metadata* const metadata) { int p; for (p = 0; p < 2; ++p) { png_infop const info = (p == 0) ? head_info : end_info; png_textp text = NULL; const png_uint_32 num = png_get_text(png, info, &text, NULL); png_uint_32 i; // Look for EXIF / XMP metadata. for (i = 0; i < num; ++i, ++text) { int j; for (j = 0; kPNGMetadataMap[j].name != NULL; ++j) { if (!strcmp(text->key, kPNGMetadataMap[j].name)) { MetadataPayload* const payload = (MetadataPayload*)((uint8_t*)metadata + kPNGMetadataMap[j].storage_offset); png_size_t text_length; switch (text->compression) { #ifdef PNG_iTXt_SUPPORTED case PNG_ITXT_COMPRESSION_NONE: case PNG_ITXT_COMPRESSION_zTXt: text_length = text->itxt_length; break; #endif case PNG_TEXT_COMPRESSION_NONE: case PNG_TEXT_COMPRESSION_zTXt: default: text_length = text->text_length; break; } if (payload->bytes != NULL) { fprintf(stderr, "Ignoring additional '%s'\n", text->key); } else if (!kPNGMetadataMap[j].process(text->text, text_length, payload)) { fprintf(stderr, "Failed to process: '%s'\n", text->key); return 0; } break; } } } // Look for an ICC profile. { png_charp name; int comp_type; #if LOCAL_PNG_PREREQ(1,5) png_bytep profile; #else png_charp profile; #endif png_uint_32 len; if (png_get_iCCP(png, info, &name, &comp_type, &profile, &len) == PNG_INFO_iCCP) { if (!MetadataCopy((const char*)profile, len, &metadata->iccp)) return 0; } } } return 1; }
/* Read a png header. */ static int png2vips_header( Read *read, VipsImage *out ) { png_uint_32 width, height; int bit_depth, color_type; int interlace_type; png_uint_32 res_x, res_y; int unit_type; png_charp name; int compression_type; png_textp text_ptr; int num_text; /* Well thank you, libpng. */ #if PNG_LIBPNG_VER < 10400 png_charp profile; #else png_bytep profile; #endif png_uint_32 proflen; int bands; VipsInterpretation interpretation; double Xres, Yres; if( setjmp( png_jmpbuf( read->pPng ) ) ) return( -1 ); png_get_IHDR( read->pPng, read->pInfo, &width, &height, &bit_depth, &color_type, &interlace_type, NULL, NULL ); /* png_get_channels() gives us 1 band for palette images ... so look * at colour_type for output bands. * * Ignore alpha, we detect that separately below. */ switch( color_type ) { case PNG_COLOR_TYPE_PALETTE: bands = 3; break; case PNG_COLOR_TYPE_GRAY_ALPHA: case PNG_COLOR_TYPE_GRAY: bands = 1; break; case PNG_COLOR_TYPE_RGB: case PNG_COLOR_TYPE_RGB_ALPHA: bands = 3; break; default: vips_error( "png2vips", "%s", _( "unsupported color type" ) ); return( -1 ); } if( bit_depth > 8 ) { if( bands < 3 ) interpretation = VIPS_INTERPRETATION_GREY16; else interpretation = VIPS_INTERPRETATION_RGB16; } else { if( bands < 3 ) interpretation = VIPS_INTERPRETATION_B_W; else interpretation = VIPS_INTERPRETATION_sRGB; } /* Expand palette images. */ if( color_type == PNG_COLOR_TYPE_PALETTE ) png_set_palette_to_rgb( read->pPng ); /* Expand transparency. */ if( png_get_valid( read->pPng, read->pInfo, PNG_INFO_tRNS ) ) { png_set_tRNS_to_alpha( read->pPng ); bands += 1; } else if( color_type == PNG_COLOR_TYPE_GRAY_ALPHA || color_type == PNG_COLOR_TYPE_RGB_ALPHA ) { /* Some images have no transparency chunk, but still set * color_type to alpha. */ bands += 1; } /* Expand <8 bit images to full bytes. */ if( color_type == PNG_COLOR_TYPE_GRAY && bit_depth < 8 ) png_set_expand_gray_1_2_4_to_8( read->pPng ); /* If we're an INTEL byte order machine and this is 16bits, we need * to swap bytes. */ if( bit_depth > 8 && !vips_amiMSBfirst() ) png_set_swap( read->pPng ); /* Get resolution. Default to 72 pixels per inch, the usual png value. */ unit_type = PNG_RESOLUTION_METER; res_x = (72 / 2.54 * 100); res_y = (72 / 2.54 * 100); png_get_pHYs( read->pPng, read->pInfo, &res_x, &res_y, &unit_type ); switch( unit_type ) { case PNG_RESOLUTION_METER: Xres = res_x / 1000.0; Yres = res_y / 1000.0; break; default: Xres = res_x; Yres = res_y; break; } /* Set VIPS header. */ vips_image_init_fields( out, width, height, bands, bit_depth > 8 ? VIPS_FORMAT_USHORT : VIPS_FORMAT_UCHAR, VIPS_CODING_NONE, interpretation, Xres, Yres ); /* Uninterlaced images will be read in seq mode. Interlaced images are * read via a huge memory buffer. */ if( interlace_type == PNG_INTERLACE_NONE ) /* Sequential mode needs thinstrip to work with things like * vips_shrink(). */ vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL ); else vips_image_pipelinev( out, VIPS_DEMAND_STYLE_ANY, NULL ); /* Fetch the ICC profile. @name is useless, something like "icc" or * "ICC Profile" etc. Ignore it. * * @profile was png_charpp in libpngs < 1.5, png_bytepp is the * modern one. Ignore the warning, if any. */ if( png_get_iCCP( read->pPng, read->pInfo, &name, &compression_type, &profile, &proflen ) ) { void *profile_copy; #ifdef DEBUG printf( "png2vips_header: attaching %d bytes of ICC profile\n", proflen ); printf( "png2vips_header: name = \"%s\"\n", name ); #endif /*DEBUG*/ if( !(profile_copy = vips_malloc( NULL, proflen )) ) return( -1 ); memcpy( profile_copy, profile, proflen ); vips_image_set_blob( out, VIPS_META_ICC_NAME, (VipsCallbackFn) vips_free, profile_copy, proflen ); } /* Sanity-check line size. */ png_read_update_info( read->pPng, read->pInfo ); if( png_get_rowbytes( read->pPng, read->pInfo ) != VIPS_IMAGE_SIZEOF_LINE( out ) ) { vips_error( "vipspng", "%s", _( "unable to read PNG header" ) ); return( -1 ); } /* Let our caller know. These are very expensive to decode. */ if( interlace_type != PNG_INTERLACE_NONE ) vips_image_set_int( out, "interlaced", 1 ); if( png_get_text( read->pPng, read->pInfo, &text_ptr, &num_text ) > 0 ) { int i; for( i = 0; i < num_text; i++ ) /* .text is always a null-terminated C string. */ if( vips__set_text( out, i, text_ptr[i].key, text_ptr[i].text ) ) return( -1 ); } return( 0 ); }