bool KPWriteImage::write2PNG(const QString& destPath) { FILE* file = fopen(QFile::encodeName(destPath), "wb"); if (!file) { kDebug( 51000 ) << "Failed to open PNG file for writing" << endl; return false; } uchar *data = 0; int bitsDepth = d->sixteenBit ? 16 : 8; png_color_8 sig_bit; png_bytep row_ptr; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info_ptr = png_create_info_struct(png_ptr); png_init_io(png_ptr, file); if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) // Intel png_set_bgr(png_ptr); else // PPC png_set_swap_alpha(png_ptr); if (d->hasAlpha) { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 8 * sizeof(uchar)]; else data = new uchar[d->width * 4 * sizeof(uchar)]; } else { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 6 * sizeof(uchar)]; else data = new uchar[d->width * 3 * sizeof(uchar)]; } sig_bit.red = bitsDepth; sig_bit.green = bitsDepth; sig_bit.blue = bitsDepth; sig_bit.alpha = bitsDepth; png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_set_compression_level(png_ptr, 9); // Write ICC profil. if (!d->iccProfile.isEmpty()) { png_set_iCCP(png_ptr, info_ptr, (png_charp)"icc", PNG_COMPRESSION_TYPE_BASE, d->iccProfile.data(), d->iccProfile.size()); } // Write Software info. QString libpngver(PNG_HEADER_VERSION_STRING); libpngver.replace('\n', ' '); QString soft = d->kipipluginsVer; soft.append(QString(" (%1)").arg(libpngver)); png_text text; text.key = (png_charp)"Software"; text.text = soft.toAscii().data(); text.compression = PNG_TEXT_COMPRESSION_zTXt; png_set_text(png_ptr, info_ptr, &(text), 1); // Store Exif data. QByteArray ba = d->metadata.getExif(true); writeRawProfile(png_ptr, info_ptr, (png_charp)"exif", ba.data(), (png_uint_32) ba.size()); // Store Iptc data. QByteArray ba2 = d->metadata.getIptc(); writeRawProfile(png_ptr, info_ptr, (png_charp)"iptc", ba2.data(), (png_uint_32) ba2.size()); // Store Xmp data. QByteArray ba3 = d->metadata.getXmp(); writeRawProfile(png_ptr, info_ptr, (png_charp)("xmp"), ba3.data(), (png_uint_32) ba3.size()); png_write_info(png_ptr, info_ptr); png_set_shift(png_ptr, &sig_bit); png_set_packing(png_ptr); uchar* ptr = (uchar*)d->data.data(); uint x, y, j; for (y = 0; y < d->height; y++) { if (cancel()) { delete [] data; fclose(file); png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); return false; } j = 0; for (x = 0; x < d->width*bytesDepth(); x+=bytesDepth()) { if (d->sixteenBit) { if (d->hasAlpha) { data[j++] = ptr[x+1]; // Blue data[j++] = ptr[ x ]; data[j++] = ptr[x+3]; // Green data[j++] = ptr[x+2]; data[j++] = ptr[x+5]; // Red data[j++] = ptr[x+4]; data[j++] = ptr[x+7]; // Alpha data[j++] = ptr[x+6]; } else { data[j++] = ptr[x+1]; // Blue data[j++] = ptr[ x ]; data[j++] = ptr[x+3]; // Green data[j++] = ptr[x+2]; data[j++] = ptr[x+5]; // Red data[j++] = ptr[x+4]; } } else { if (d->hasAlpha) { data[j++] = ptr[ x ]; // Blue data[j++] = ptr[x+1]; // Green data[j++] = ptr[x+2]; // Red data[j++] = ptr[x+3]; // Alpha } else { data[j++] = ptr[ x ]; // Blue data[j++] = ptr[x+1]; // Green data[j++] = ptr[x+2]; // Red } } } row_ptr = (png_bytep) data; png_write_rows(png_ptr, &row_ptr, 1); ptr += (d->width * bytesDepth()); } delete [] data; png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); fclose(file); return true; }
DEF_TEST(Codec_pngChunkReader, r) { // Create a dummy bitmap. Use unpremul RGBA for libpng. SkBitmap bm; const int w = 1; const int h = 1; const SkImageInfo bmInfo = SkImageInfo::Make(w, h, kRGBA_8888_SkColorType, kUnpremul_SkAlphaType); bm.setInfo(bmInfo); bm.allocPixels(); bm.eraseColor(SK_ColorBLUE); SkMD5::Digest goodDigest; md5(bm, &goodDigest); // Write to a png file. png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); REPORTER_ASSERT(r, png); if (!png) { return; } png_infop info = png_create_info_struct(png); REPORTER_ASSERT(r, info); if (!info) { png_destroy_write_struct(&png, nullptr); return; } if (setjmp(png_jmpbuf(png))) { ERRORF(r, "failed writing png"); png_destroy_write_struct(&png, &info); return; } SkDynamicMemoryWStream wStream; png_set_write_fn(png, (void*) (&wStream), codex_test_write_fn, nullptr); png_set_IHDR(png, info, (png_uint_32)w, (png_uint_32)h, 8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Create some chunks that match the Android framework's use. static png_unknown_chunk gUnknowns[] = { { "npOl", (png_byte*)"outline", sizeof("outline"), PNG_HAVE_IHDR }, { "npLb", (png_byte*)"layoutBounds", sizeof("layoutBounds"), PNG_HAVE_IHDR }, { "npTc", (png_byte*)"ninePatchData", sizeof("ninePatchData"), PNG_HAVE_IHDR }, }; png_set_keep_unknown_chunks(png, PNG_HANDLE_CHUNK_ALWAYS, (png_byte*)"npOl\0npLb\0npTc\0", 3); png_set_unknown_chunks(png, info, gUnknowns, SK_ARRAY_COUNT(gUnknowns)); #if PNG_LIBPNG_VER < 10600 /* Deal with unknown chunk location bug in 1.5.x and earlier */ png_set_unknown_chunk_location(png, info, 0, PNG_HAVE_IHDR); png_set_unknown_chunk_location(png, info, 1, PNG_HAVE_IHDR); #endif png_write_info(png, info); for (int j = 0; j < h; j++) { png_bytep row = (png_bytep)(bm.getAddr(0, j)); png_write_rows(png, &row, 1); } png_write_end(png, info); png_destroy_write_struct(&png, &info); class ChunkReader : public SkPngChunkReader { public: ChunkReader(skiatest::Reporter* r) : fReporter(r) { this->reset(); } bool readChunk(const char tag[], const void* data, size_t length) override { for (size_t i = 0; i < SK_ARRAY_COUNT(gUnknowns); ++i) { if (!strcmp(tag, (const char*) gUnknowns[i].name)) { // Tag matches. This should have been the first time we see it. REPORTER_ASSERT(fReporter, !fSeen[i]); fSeen[i] = true; // Data and length should match REPORTER_ASSERT(fReporter, length == gUnknowns[i].size); REPORTER_ASSERT(fReporter, !strcmp((const char*) data, (const char*) gUnknowns[i].data)); return true; } } ERRORF(fReporter, "Saw an unexpected unknown chunk."); return true; } bool allHaveBeenSeen() { bool ret = true; for (auto seen : fSeen) { ret &= seen; } return ret; } void reset() { sk_bzero(fSeen, sizeof(fSeen)); } private: skiatest::Reporter* fReporter; // Unowned bool fSeen[3]; }; ChunkReader chunkReader(r); // Now read the file with SkCodec. SkAutoTUnref<SkData> data(wStream.copyToData()); SkAutoTDelete<SkCodec> codec(SkCodec::NewFromData(data, &chunkReader)); REPORTER_ASSERT(r, codec); if (!codec) { return; } // Now compare to the original. SkBitmap decodedBm; decodedBm.setInfo(codec->getInfo()); decodedBm.allocPixels(); SkCodec::Result result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes()); REPORTER_ASSERT(r, SkCodec::kSuccess == result); if (decodedBm.colorType() != bm.colorType()) { SkBitmap tmp; bool success = decodedBm.copyTo(&tmp, bm.colorType()); REPORTER_ASSERT(r, success); if (!success) { return; } tmp.swap(decodedBm); } compare_to_good_digest(r, goodDigest, decodedBm); REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); // Decoding again will read the chunks again. chunkReader.reset(); REPORTER_ASSERT(r, !chunkReader.allHaveBeenSeen()); result = codec->getPixels(codec->getInfo(), decodedBm.getPixels(), decodedBm.rowBytes()); REPORTER_ASSERT(r, SkCodec::kSuccess == result); REPORTER_ASSERT(r, chunkReader.allHaveBeenSeen()); }
/*! * pixWriteStreamPng() * * Input: stream * pix * gamma (use 0.0 if gamma is not defined) * Return: 0 if OK; 1 on error * * Notes: * (1) If called from pixWriteStream(), the stream is positioned * at the beginning of the file. * (2) To do sequential writes of png format images to a stream, * use pixWriteStreamPng() directly. * (3) gamma is an optional png chunk. If no gamma value is to be * placed into the file, use gamma = 0.0. Otherwise, if * gamma > 0.0, its value is written into the header. * (4) The use of gamma in png is highly problematic. For an illuminating * discussion, see: http://hsivonen.iki.fi/png-gamma/ * (5) What is the effect/meaning of gamma in the png file? This * gamma, which we can call the 'source' gamma, is the * inverse of the gamma that was used in enhance.c to brighten * or darken images. The 'source' gamma is supposed to indicate * the intensity mapping that was done at the time the * image was captured. Display programs typically apply a * 'display' gamma of 2.2 to the output, which is intended * to linearize the intensity based on the response of * thermionic tubes (CRTs). Flat panel LCDs have typically * been designed to give a similar response as CRTs (call it * "backward compatibility"). The 'display' gamma is * in some sense the inverse of the 'source' gamma. * jpeg encoders attached to scanners and cameras will lighten * the pixels, applying a gamma corresponding to approximately * a square-root relation of output vs input: * output = input^(gamma) * where gamma is often set near 0.4545 (1/gamma is 2.2). * This is stored in the image file. Then if the display * program reads the gamma, it will apply a display gamma, * typically about 2.2; the product is 1.0, and the * display program produces a linear output. This works because * the dark colors were appropriately boosted by the scanner, * as described by the 'source' gamma, so they should not * be further boosted by the display program. * (6) As an example, with xv and display, if no gamma is stored, * the program acts as if gamma were 0.4545, multiplies this by 2.2, * and does a linear rendering. Taking this as a baseline * brightness, if the stored gamma is: * > 0.4545, the image is rendered lighter than baseline * < 0.4545, the image is rendered darker than baseline * In contrast, gqview seems to ignore the gamma chunk in png. * (7) The only valid pixel depths in leptonica are 1, 2, 4, 8, 16 * and 32. However, it is possible, and in some cases desirable, * to write out a png file using an rgb pix that has 24 bpp. * For example, the open source xpdf SplashBitmap class generates * 24 bpp rgb images. Consequently, we anble writing 24 bpp pix. * To generate such a pix, you can make a 24 bpp pix without data * and assign the data array to the pix; e.g., * pix = pixCreateHeader(w, h, 24); * pixSetData(pix, rgbdata); * See pixConvert32To24() for an example, where we get rgbdata * from the 32 bpp pix. Caution: do not call pixSetPadBits(), * because the alignment is wrong and you may erase part of the * last pixel on each line. */ l_int32 pixWriteStreamPng(FILE *fp, PIX *pix, l_float32 gamma) { char commentstring[] = "Comment"; l_int32 i, j, k; l_int32 wpl, d, cmflag; l_int32 ncolors; l_int32 *rmap, *gmap, *bmap; l_uint32 *data, *ppixel; png_byte bit_depth, color_type; png_uint_32 w, h; png_uint_32 xres, yres; png_bytep *row_pointers; png_bytep rowbuffer; png_structp png_ptr; png_infop info_ptr; png_colorp palette; PIX *pixt; PIXCMAP *cmap; char *text; PROCNAME("pixWriteStreamPng"); if (!fp) return ERROR_INT("stream not open", procName, 1); if (!pix) return ERROR_INT("pix not defined", procName, 1); /* Allocate the 2 data structures */ if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL)) == NULL) return ERROR_INT("png_ptr not made", procName, 1); if ((info_ptr = png_create_info_struct(png_ptr)) == NULL) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); return ERROR_INT("info_ptr not made", procName, 1); } /* Set up png setjmp error handling */ if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return ERROR_INT("internal png error", procName, 1); } png_init_io(png_ptr, fp); /* With best zlib compression (9), get between 1 and 10% improvement * over default (5), but the compression is 3 to 10 times slower. * Our default compression is the zlib default (5). */ png_set_compression_level(png_ptr, var_ZLIB_COMPRESSION); w = pixGetWidth(pix); h = pixGetHeight(pix); d = pixGetDepth(pix); if ((cmap = pixGetColormap(pix))) cmflag = 1; else cmflag = 0; /* Set the color type and bit depth. */ if (d == 32 && var_PNG_WRITE_ALPHA == 1) { bit_depth = 8; color_type = PNG_COLOR_TYPE_RGBA; /* 6 */ cmflag = 0; /* ignore if it exists */ } else if (d == 24 || d == 32) { bit_depth = 8; color_type = PNG_COLOR_TYPE_RGB; /* 2 */ cmflag = 0; /* ignore if it exists */ } else { bit_depth = d; color_type = PNG_COLOR_TYPE_GRAY; /* 0 */ } if (cmflag) color_type = PNG_COLOR_TYPE_PALETTE; /* 3 */ #if DEBUG fprintf(stderr, "cmflag = %d, bit_depth = %d, color_type = %d\n", cmflag, bit_depth, color_type); #endif /* DEBUG */ png_set_IHDR(png_ptr, info_ptr, w, h, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Store resolution in ppm, if known */ xres = (png_uint_32)(39.37 * (l_float32)pixGetXRes(pix) + 0.5); yres = (png_uint_32)(39.37 * (l_float32)pixGetYRes(pix) + 0.5); if ((xres == 0) || (yres == 0)) png_set_pHYs(png_ptr, info_ptr, 0, 0, PNG_RESOLUTION_UNKNOWN); else png_set_pHYs(png_ptr, info_ptr, xres, yres, PNG_RESOLUTION_METER); if (cmflag) { pixcmapToArrays(cmap, &rmap, &gmap, &bmap); ncolors = pixcmapGetCount(cmap); /* Make and save the palette */ if ((palette = (png_colorp)(CALLOC(ncolors, sizeof(png_color)))) == NULL) return ERROR_INT("palette not made", procName, 1); for (i = 0; i < ncolors; i++) { palette[i].red = (png_byte)rmap[i]; palette[i].green = (png_byte)gmap[i]; palette[i].blue = (png_byte)bmap[i]; } png_set_PLTE(png_ptr, info_ptr, palette, (int)ncolors); FREE(rmap); FREE(gmap); FREE(bmap); } /* 0.4545 is treated as the default by some image * display programs (not gqview). A value > 0.4545 will * lighten an image as displayed by xv, display, etc. */ if (gamma > 0.0) png_set_gAMA(png_ptr, info_ptr, (l_float64)gamma); if ((text = pixGetText(pix))) { png_text text_chunk; text_chunk.compression = PNG_TEXT_COMPRESSION_NONE; text_chunk.key = commentstring; text_chunk.text = text; text_chunk.text_length = strlen(text); #ifdef PNG_ITXT_SUPPORTED text_chunk.itxt_length = 0; text_chunk.lang = NULL; text_chunk.lang_key = NULL; #endif png_set_text(png_ptr, info_ptr, &text_chunk, 1); } /* Write header and palette info */ png_write_info(png_ptr, info_ptr); if ((d != 32) && (d != 24)) { /* not rgb color */ /* Generate a temporary pix with bytes swapped. * For a binary image, there are two conditions in * which you must first invert the data for writing png: * (a) no colormap * (b) colormap with BLACK set to 0 * png writes binary with BLACK = 0, unless contradicted * by a colormap. If the colormap has BLACK = "1" * (typ. about 255), do not invert the data. If there * is no colormap, you must invert the data to store * in default BLACK = 0 state. */ if (d == 1 && (!cmap || (cmap && ((l_uint8 *)(cmap->array))[0] == 0x0))) { pixt = pixInvert(NULL, pix); pixEndianByteSwap(pixt); } else pixt = pixEndianByteSwapNew(pix); if (!pixt) { png_destroy_write_struct(&png_ptr, &info_ptr); return ERROR_INT("pixt not made", procName, 1); } /* Make and assign array of image row pointers */ if ((row_pointers = (png_bytep *)CALLOC(h, sizeof(png_bytep))) == NULL) return ERROR_INT("row-pointers not made", procName, 1); wpl = pixGetWpl(pixt); data = pixGetData(pixt); for (i = 0; i < h; i++) row_pointers[i] = (png_bytep)(data + i * wpl); png_set_rows(png_ptr, info_ptr, row_pointers); /* Transfer the data */ png_write_image(png_ptr, row_pointers); png_write_end(png_ptr, info_ptr); if (cmflag) FREE(palette); FREE(row_pointers); pixDestroy(&pixt); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; } /* For rgb, compose and write a row at a time */ data = pixGetData(pix); wpl = pixGetWpl(pix); if (d == 24) { /* See note 7 above: special case of 24 bpp rgb */ for (i = 0; i < h; i++) { ppixel = data + i * wpl; png_write_rows(png_ptr, (png_bytepp)&ppixel, 1); } } else { /* 32 bpp rgb and rgba */ if ((rowbuffer = (png_bytep)CALLOC(w, 4)) == NULL) return ERROR_INT("rowbuffer not made", procName, 1); for (i = 0; i < h; i++) { ppixel = data + i * wpl; for (j = k = 0; j < w; j++) { rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_RED); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_GREEN); rowbuffer[k++] = GET_DATA_BYTE(ppixel, COLOR_BLUE); if (var_PNG_WRITE_ALPHA == 1) rowbuffer[k++] = GET_DATA_BYTE(ppixel, L_ALPHA_CHANNEL); ppixel++; } png_write_rows(png_ptr, &rowbuffer, 1); } FREE(rowbuffer); } png_write_end(png_ptr, info_ptr); if (cmflag) FREE(palette); png_destroy_write_struct(&png_ptr, &info_ptr); return 0; }
void PNGImageFileWriter::write( const char* filename, const ICanvas& image, const ImageAttributes& image_attributes) { // Retrieve canvas properties. const CanvasProperties& props = image.properties(); // todo: lift these limitations. assert(props.m_channel_count == 3 || props.m_channel_count == 4); // Open the file in write mode. FILE* fp = fopen(filename, "wb"); if (fp == 0) throw ExceptionIOError(); // Allocate and initialize the png_struct structure. png_structp png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, fp, error_callback, warning_callback); if (png_ptr == 0) { fclose(fp); throw ExceptionMemoryError(); } // Allocate the png_info structure. png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == 0) { png_destroy_write_struct(&png_ptr, 0); fclose(fp); throw ExceptionMemoryError(); } // Set up the output control. png_init_io(png_ptr, fp); // Set image information. png_set_IHDR( png_ptr, info_ptr, static_cast<png_uint_32>(props.m_canvas_width), static_cast<png_uint_32>(props.m_canvas_height), 8, // bit depth -- todo: allow higher bit depths props.m_channel_count == 4 ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); // Mark the image as being sRGB (implying specific gamma and color matching functions). // See http://www.vias.org/pngguide/chapter10_07.html for details about intents. png_set_sRGB_gAMA_and_cHRM( png_ptr, info_ptr, PNG_sRGB_INTENT_PERCEPTUAL); // todo: allow the user to select different intents // Set the number of significant bits for each of the R, G, B and A channels. // todo: are we required to provide these information? png_color_8 sig_bit; sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; sig_bit.alpha = 8; sig_bit.gray = 8; // for completeness png_set_sBIT(png_ptr, info_ptr, &sig_bit); // Add image attributes. vector<png_text_struct> text_chunks; add_attributes( png_ptr, info_ptr, text_chunks, image_attributes); if (!text_chunks.empty()) { png_set_text( png_ptr, info_ptr, &text_chunks[0], static_cast<int>(text_chunks.size())); } // Write the file header information. png_write_info(png_ptr, info_ptr); // Create the temporary buffer holding one row of tiles in target format. vector<uint8> buffer( props.m_canvas_width * props.m_tile_height * props.m_channel_count); // Construct pointers to each row of the temporary buffer. vector<uint8*> buffer_rows(props.m_tile_height); for (size_t y = 0; y < props.m_tile_height; ++y) buffer_rows[y] = &buffer[y * props.m_canvas_width * props.m_channel_count]; // Loop over the rows of tiles. for (size_t tile_y = 0; tile_y < props.m_tile_count_y; ++tile_y) { // Convert this row of tiles to target format. for (size_t tile_x = 0; tile_x < props.m_tile_count_x; ++tile_x) { const Tile& tile = image.tile(tile_x, tile_y); assert(tile.get_height() <= props.m_tile_height); for (size_t y = 0; y < tile.get_height(); ++y) { for (size_t x = 0; x < tile.get_width(); ++x) { // Horizontal coordinate of the pixel in the temporary buffer. const size_t buffer_x = tile_x * props.m_tile_width + x; // Index of the pixel in the temporary buffer. const size_t buffer_index = (y * props.m_canvas_width + buffer_x) * props.m_channel_count; // Fetch the pixel at coordinates (x, y) in the tile, // perform format conversion if necessary, and store // the converted pixel into the temporary buffer. if (tile.get_channel_count() == 3) { Color3i pixel; tile.get_pixel(x, y, pixel); buffer[buffer_index + 0] = pixel[0]; buffer[buffer_index + 1] = pixel[1]; buffer[buffer_index + 2] = pixel[2]; } else { assert(tile.get_channel_count() == 4); Color4i pixel; tile.get_pixel(x, y, pixel); buffer[buffer_index + 0] = pixel[0]; buffer[buffer_index + 1] = pixel[1]; buffer[buffer_index + 2] = pixel[2]; buffer[buffer_index + 3] = pixel[3]; } } } } // Write this row of tiles to the file. const size_t row_count = image.tile(0, tile_y).get_height(); png_write_rows( png_ptr, &buffer_rows[0], static_cast<png_uint_32>(row_count)); } // Finish writing the file. png_write_end(png_ptr, 0); // Deallocate the png_struct and png_info structures. png_destroy_write_struct(&png_ptr, &info_ptr); // Deallocate text chunks. destroy_text_chunks(text_chunks); // Close the file. fclose(fp); }
bool writePixelsToBuffer(unsigned char *pixels, unsigned width, unsigned height, unsigned numChannels, bool flipped, char **buffer, int *size) { struct png_tmp_buffer png_mem; png_structp png_ptr; png_infop info_ptr; int type; png_mem.buffer = NULL; png_mem.size = 0; switch (numChannels) { case 4: type = PNG_COLOR_TYPE_RGB_ALPHA; break; case 3: type = PNG_COLOR_TYPE_RGB; break; case 2: type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 1: type = PNG_COLOR_TYPE_GRAY; break; default: goto no_png; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) goto no_png; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, NULL); goto no_png; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); goto no_png; } png_set_write_fn(png_ptr, &png_mem, pngWriteCallback, NULL); png_set_IHDR(png_ptr, info_ptr, width, height, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION); png_write_info(png_ptr, info_ptr); if (!flipped) { for (unsigned y = 0; y < height; ++y) { png_bytep row = (png_bytep)(pixels + y*width*numChannels); png_write_rows(png_ptr, &row, 1); } } else { unsigned y = height; while (y--) { png_bytep row = (png_bytep)(pixels + y*width*numChannels); png_write_rows(png_ptr, &row, 1); } } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); *buffer = png_mem.buffer; *size = png_mem.size; return true; no_png: *buffer = NULL; *size = 0; if (png_mem.buffer) free(png_mem.buffer); return false; }
bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose ) { wxPNGInfoStruct wxinfo; wxinfo.verbose = verbose; wxinfo.stream.out = &stream; png_structp png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING, NULL, wx_png_error, wx_png_warning ); if (!png_ptr) { if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } if (setjmp(wxinfo.jmpbuf)) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } // NB: please see the comment near wxPNGInfoStruct declaration for // explanation why this line is mandatory png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL); const int iColorType = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT) ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT) : wxPNG_TYPE_COLOUR; const int iBitDepth = image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH) ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH) : 8; bool bHasAlpha = image->HasAlpha(); bool bHasMask = image->HasMask(); bool bUseAlpha = bHasAlpha || bHasMask; int iPngColorType; if ( iColorType==wxPNG_TYPE_COLOUR ) { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; } else { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; } if (image->HasOption(wxIMAGE_OPTION_PNG_FILTER)) png_set_filter( png_ptr, PNG_FILTER_TYPE_BASE, image->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL)) png_set_compression_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL)) png_set_compression_mem_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY)) png_set_compression_strategy( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE)) png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) ); png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), iBitDepth, iPngColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); int iElements; png_color_8 sig_bit; if ( iPngColorType & PNG_COLOR_MASK_COLOR ) { sig_bit.red = sig_bit.green = sig_bit.blue = (png_byte)iBitDepth; iElements = 3; } else // grey { sig_bit.gray = (png_byte)iBitDepth; iElements = 1; } if ( iPngColorType & PNG_COLOR_MASK_ALPHA ) { sig_bit.alpha = (png_byte)iBitDepth; iElements++; } if ( iBitDepth == 16 ) iElements *= 2; // save the image resolution if we have it int resX, resY; switch ( GetResolutionFromOptions(*image, &resX, &resY) ) { case wxIMAGE_RESOLUTION_INCHES: { const double INCHES_IN_METER = 10000.0 / 254; resX = int(resX * INCHES_IN_METER); resY = int(resY * INCHES_IN_METER); } break; case wxIMAGE_RESOLUTION_CM: resX *= 100; resY *= 100; break; case wxIMAGE_RESOLUTION_NONE: break; default: wxFAIL_MSG( wxT("unsupported image resolution units") ); } if ( resX && resY ) png_set_pHYs( png_ptr, info_ptr, resX, resY, PNG_RESOLUTION_METER ); png_set_sBIT( png_ptr, info_ptr, &sig_bit ); png_write_info( png_ptr, info_ptr ); png_set_shift( png_ptr, &sig_bit ); png_set_packing( png_ptr ); unsigned char * data = (unsigned char *)malloc( image->GetWidth() * iElements ); if ( !data ) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); return false; } unsigned char * pAlpha = (unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL); int iHeight = image->GetHeight(); int iWidth = image->GetWidth(); unsigned char uchMaskRed = 0, uchMaskGreen = 0, uchMaskBlue = 0; if ( bHasMask ) { uchMaskRed = image->GetMaskRed(); uchMaskGreen = image->GetMaskGreen(); uchMaskBlue = image->GetMaskBlue(); } unsigned char *pColors = image->GetData(); for (int y = 0; y != iHeight; ++y) { unsigned char *pData = data; for (int x = 0; x != iWidth; x++) { unsigned char uchRed = *pColors++; unsigned char uchGreen = *pColors++; unsigned char uchBlue = *pColors++; switch ( iColorType ) { default: wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") ); // fall through case wxPNG_TYPE_COLOUR: *pData++ = uchRed; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = uchGreen; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = uchBlue; if ( iBitDepth == 16 ) *pData++ = 0; break; case wxPNG_TYPE_GREY: { // where do these coefficients come from? maybe we // should have image options for them as well? unsigned uiColor = (unsigned) (76.544*(unsigned)uchRed + 150.272*(unsigned)uchGreen + 36.864*(unsigned)uchBlue); *pData++ = (unsigned char)((uiColor >> 8) & 0xFF); if ( iBitDepth == 16 ) *pData++ = (unsigned char)(uiColor & 0xFF); } break; case wxPNG_TYPE_GREY_RED: *pData++ = uchRed; if ( iBitDepth == 16 ) *pData++ = 0; break; } if ( bUseAlpha ) { unsigned char uchAlpha = 255; if ( bHasAlpha ) uchAlpha = *pAlpha++; if ( bHasMask ) { if ( (uchRed == uchMaskRed) && (uchGreen == uchMaskGreen) && (uchBlue == uchMaskBlue) ) uchAlpha = 0; } *pData++ = uchAlpha; if ( iBitDepth == 16 ) *pData++ = 0; } } png_bytep row_ptr = data; png_write_rows( png_ptr, &row_ptr, 1 ); } free(data); png_write_end( png_ptr, info_ptr ); png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr ); return true; }
static bool sp_png_write_rgba_striped(SPDocument *doc, gchar const *filename, unsigned long int width, unsigned long int height, double xdpi, double ydpi, int (* get_rows)(guchar const **rows, void **to_free, int row, int num_rows, void *data), void *data) { struct SPEBP *ebp = (struct SPEBP *) data; FILE *fp; png_structp png_ptr; png_infop info_ptr; png_color_8 sig_bit; png_uint_32 r; g_return_val_if_fail(filename != NULL, false); g_return_val_if_fail(data != NULL, false); /* open the file */ Inkscape::IO::dump_fopen_call(filename, "M"); fp = Inkscape::IO::fopen_utf8name(filename, "wb"); g_return_val_if_fail(fp != NULL, false); /* Create and initialize the png_struct with the desired error handler * functions. If you want to use the default stderr and longjump method, * you can supply NULL for the last three parameters. We also check that * the library version is compatible with the one used at compile time, * in case we are using dynamically linked libraries. REQUIRED. */ png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fclose(fp); return false; } /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(fp); png_destroy_write_struct(&png_ptr, NULL); return false; } /* Set error handling. REQUIRED if you aren't supplying your own * error hadnling functions in the png_create_write_struct() call. */ if (setjmp(png_jmpbuf(png_ptr))) { // If we get here, we had a problem reading the file fclose(fp); png_destroy_write_struct(&png_ptr, &info_ptr); return false; } /* set up the output control if you are using standard C streams */ png_init_io(png_ptr, fp); /* Set the image information here. Width and height are up to 2^31, * bit_depth is one of 1, 2, 4, 8, or 16, but valid values also depend on * the color_type selected. color_type is one of PNG_COLOR_TYPE_GRAY, * PNG_COLOR_TYPE_GRAY_ALPHA, PNG_COLOR_TYPE_PALETTE, PNG_COLOR_TYPE_RGB, * or PNG_COLOR_TYPE_RGB_ALPHA. interlace is either PNG_INTERLACE_NONE or * PNG_INTERLACE_ADAM7, and the compression_type and filter_type MUST * currently be PNG_COMPRESSION_TYPE_BASE and PNG_FILTER_TYPE_BASE. REQUIRED */ png_set_IHDR(png_ptr, info_ptr, width, height, 8, /* bit_depth */ PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* otherwise, if we are dealing with a color image then */ sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; /* if the image has an alpha channel then */ sig_bit.alpha = 8; png_set_sBIT(png_ptr, info_ptr, &sig_bit); PngTextList textList; textList.add("Software", "www.inkscape.org"); // Made by Inkscape comment { const gchar* pngToDc[] = {"Title", "title", "Author", "creator", "Description", "description", //"Copyright", "", "Creation Time", "date", //"Disclaimer", "", //"Warning", "", "Source", "source" //"Comment", "" }; for (size_t i = 0; i < G_N_ELEMENTS(pngToDc); i += 2) { struct rdf_work_entity_t * entity = rdf_find_entity ( pngToDc[i + 1] ); if (entity) { gchar const* data = rdf_get_work_entity(doc, entity); if (data && *data) { textList.add(pngToDc[i], data); } } else { g_warning("Unable to find entity [%s]", pngToDc[i + 1]); } } struct rdf_license_t *license = rdf_get_license(doc); if (license) { if (license->name && license->uri) { gchar* tmp = g_strdup_printf("%s %s", license->name, license->uri); textList.add("Copyright", tmp); g_free(tmp); } else if (license->name) { textList.add("Copyright", license->name); } else if (license->uri) { textList.add("Copyright", license->uri); } } } if (textList.getCount() > 0) { png_set_text(png_ptr, info_ptr, textList.getPtext(), textList.getCount()); } /* other optional chunks like cHRM, bKGD, tRNS, tIME, oFFs, pHYs, */ /* note that if sRGB is present the cHRM chunk must be ignored * on read and must be written in accordance with the sRGB profile */ png_set_pHYs(png_ptr, info_ptr, unsigned(xdpi / 0.0254 + 0.5), unsigned(ydpi / 0.0254 + 0.5), PNG_RESOLUTION_METER); /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); /* Once we write out the header, the compression type on the text * chunks gets changed to PNG_TEXT_COMPRESSION_NONE_WR or * PNG_TEXT_COMPRESSION_zTXt_WR, so it doesn't get written out again * at the end. */ /* set up the transformations you want. Note that these are * all optional. Only call them if you want them. */ /* --- CUT --- */ /* The easiest way to write the image (you may have a different memory * layout, however, so choose what fits your needs best). You need to * use the first method if you aren't handling interlacing yourself. */ png_bytep* row_pointers = new png_bytep[ebp->sheight]; r = 0; while (r < static_cast<png_uint_32>(height)) { void *to_free; int n = get_rows((unsigned char const **) row_pointers, &to_free, r, height-r, data); if (!n) break; png_write_rows(png_ptr, row_pointers, n); g_free(to_free); r += n; } delete[] row_pointers; /* You can write optional chunks like tEXt, zTXt, and tIME at the end * as well. */ /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); /* if you allocated any text comments, free them here */ /* clean up after the write, and free any memory allocated */ png_destroy_write_struct(&png_ptr, &info_ptr); /* close the file */ fclose(fp); /* that's it */ return true; }
RADRT_API bool RADRT_CALL Encode(const Image &in, const Mipmap &mip, bool interlaced, void *&outData, AddrSize &outSize) { RAD_ASSERT(in.format == Format_A8 || in.format == Format_RGB888 || in.format == Format_RGBA8888); if (in.format != Format_A8 && in.format != Format_RGB888 && in.format != Format_RGBA8888) return false; png_structp png; png_infop info; png = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, 0, PNGErrorHandler, PNGWarningHandler, 0, PNGMalloc, PNGFree); if (!png) return false; info = png_create_info_struct(png); if (!info) { png_destroy_write_struct(&png, 0); return false; } stream::DynamicMemOutputBuffer ob(ZImageCodec); stream::OutputStream os(ob); png_set_write_fn(png, &os, PNGWrite, PNGFlush); png_set_IHDR(png, info, mip.width, mip.height, 8, (in.format == Format_A8) ? PNG_COLOR_TYPE_GRAY : (in.format == Format_RGB888) ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGBA, interlaced ? PNG_INTERLACE_ADAM7 : PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_color_8 sig; memset(&sig, 0, sizeof(sig)); if (in.format == Format_A8) { sig.gray = 8; } else { sig.red = 8; sig.green = 8; sig.blue = 8; sig.alpha = (in.format == Format_RGBA8888) ? 8 : 0; } png_set_sBIT(png, info, &sig); try { png_write_info(png, info); } catch (PNGException&) { png_destroy_write_struct(&png, &info); return false; } png_set_shift(png, &sig); int passes = 1; if (interlaced) { passes = png_set_interlace_handling(png); } for (int pass = 0; pass < passes; ++pass) { png_bytep src = (png_bytep)mip.data; for (int y = 0; y < mip.height; ++y) { png_write_rows(png, (png_bytepp)&src, 1); src += mip.stride; } } png_write_end(png, info); png_destroy_write_struct(&png, &info); outData = ob.OutputBuffer().Ptr(); outSize = ob.OutputBuffer().Size(); ob.OutputBuffer().Set(0, 0); // don't let the output buffer release the memory. return true; }
bool Q_INTERNAL_WIN_NO_THROW QPNGImageWriter::writeImage(const QImage& image, volatile int quality_in, const QString &description, int off_x_in, int off_y_in) { QPoint offset = image.offset(); int off_x = off_x_in + offset.x(); int off_y = off_y_in + offset.y(); png_structp png_ptr; png_infop info_ptr; png_ptr = png_create_write_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_write_struct(&png_ptr, 0); return false; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); return false; } int quality = quality_in; if (quality >= 0) { if (quality > 9) { qWarning("PNG: Quality %d out of range", quality); quality = 9; } png_set_compression_level(png_ptr, quality); } png_set_write_fn(png_ptr, (void*)this, qpiw_write_fn, qpiw_flush_fn); int color_type = 0; if (image.colorCount()) { if (image.isGrayscale()) color_type = PNG_COLOR_TYPE_GRAY; else color_type = PNG_COLOR_TYPE_PALETTE; } else if (image.format() == QImage::Format_Grayscale8) color_type = PNG_COLOR_TYPE_GRAY; else if (image.hasAlphaChannel()) color_type = PNG_COLOR_TYPE_RGB_ALPHA; else color_type = PNG_COLOR_TYPE_RGB; png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), image.depth() == 1 ? 1 : 8, // per channel color_type, 0, 0, 0); // sets #channels if (gamma != 0.0) { png_set_gAMA(png_ptr, info_ptr, 1.0/gamma); } if (image.format() == QImage::Format_MonoLSB) png_set_packswap(png_ptr); if (color_type == PNG_COLOR_TYPE_PALETTE) { // Paletted int num_palette = qMin(256, image.colorCount()); png_color palette[256]; png_byte trans[256]; int num_trans = 0; for (int i=0; i<num_palette; i++) { QRgb rgba=image.color(i); palette[i].red = qRed(rgba); palette[i].green = qGreen(rgba); palette[i].blue = qBlue(rgba); trans[i] = qAlpha(rgba); if (trans[i] < 255) { num_trans = i+1; } } png_set_PLTE(png_ptr, info_ptr, palette, num_palette); if (num_trans) { png_set_tRNS(png_ptr, info_ptr, trans, num_trans, 0); } } // Swap ARGB to RGBA (normal PNG format) before saving on // BigEndian machines if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { png_set_swap_alpha(png_ptr); } // Qt==ARGB==Big(ARGB)==Little(BGRA). But RGB888 is RGB regardless if (QSysInfo::ByteOrder == QSysInfo::LittleEndian && image.format() != QImage::Format_RGB888) { png_set_bgr(png_ptr); } if (off_x || off_y) { png_set_oFFs(png_ptr, info_ptr, off_x, off_y, PNG_OFFSET_PIXEL); } if (frames_written > 0) png_set_sig_bytes(png_ptr, 8); if (image.dotsPerMeterX() > 0 || image.dotsPerMeterY() > 0) { png_set_pHYs(png_ptr, info_ptr, image.dotsPerMeterX(), image.dotsPerMeterY(), PNG_RESOLUTION_METER); } set_text(image, png_ptr, info_ptr, description); png_write_info(png_ptr, info_ptr); if (image.depth() != 1) png_set_packing(png_ptr); if (color_type == PNG_COLOR_TYPE_RGB && image.format() != QImage::Format_RGB888) png_set_filler(png_ptr, 0, QSysInfo::ByteOrder == QSysInfo::BigEndian ? PNG_FILLER_BEFORE : PNG_FILLER_AFTER); if (looping >= 0 && frames_written == 0) { uchar data[13] = "NETSCAPE2.0"; // 0123456789aBC data[0xB] = looping%0x100; data[0xC] = looping/0x100; png_write_chunk(png_ptr, const_cast<png_bytep>((const png_byte *)"gIFx"), data, 13); } if (ms_delay >= 0 || disposal!=Unspecified) { uchar data[4]; data[0] = disposal; data[1] = 0; data[2] = (ms_delay/10)/0x100; // hundredths data[3] = (ms_delay/10)%0x100; png_write_chunk(png_ptr, const_cast<png_bytep>((const png_byte *)"gIFg"), data, 4); } int height = image.height(); int width = image.width(); switch (image.format()) { case QImage::Format_Mono: case QImage::Format_MonoLSB: case QImage::Format_Indexed8: case QImage::Format_Grayscale8: case QImage::Format_RGB32: case QImage::Format_ARGB32: case QImage::Format_RGB888: { png_bytep* row_pointers = new png_bytep[height]; for (int y=0; y<height; y++) row_pointers[y] = const_cast<png_bytep>(image.constScanLine(y)); png_write_image(png_ptr, row_pointers); delete [] row_pointers; } break; default: { QImage::Format fmt = image.hasAlphaChannel() ? QImage::Format_ARGB32 : QImage::Format_RGB32; QImage row; png_bytep row_pointers[1]; for (int y=0; y<height; y++) { row = image.copy(0, y, width, 1).convertToFormat(fmt); row_pointers[0] = const_cast<png_bytep>(row.constScanLine(0)); png_write_rows(png_ptr, row_pointers, 1); } } break; } png_write_end(png_ptr, info_ptr); frames_written++; png_destroy_write_struct(&png_ptr, &info_ptr); return true; }
void image::write_png(std::string filename) { #ifdef HAVE_LIBPNG INFO("Writing PNG file: " << filename); //open file FILE *fp = fopen(filename.c_str(), "wb"); if (!fp) throw image_file_exception("image::write_png - could not open file: " + filename); //create structures png_structp png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING, (png_voidp)NULL, NULL, NULL); if(!png_ptr) throw image_write_exception("image::write_png - Couldnt create PNG write structure"); png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); throw image_write_exception("image::write_png - Couldnt create PNG info structure"); } //handle errors if(setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); throw image_read_exception("image::load_png - PNG error"); } //FIXME - should we have a write row function call back? //set the io png_init_io(png_ptr, fp); //set up the header info png_set_IHDR(png_ptr, info_ptr, offscreen_surface->rect().width(), offscreen_surface->rect().height(), 8/*bit depth*/, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); //set color palette png_colorp palette = (png_colorp)png_malloc(png_ptr, PNG_MAX_PALETTE_LENGTH * sizeof (png_color)); png_set_PLTE(png_ptr, info_ptr, palette, PNG_MAX_PALETTE_LENGTH); //write header information png_write_info(png_ptr, info_ptr); //write image unsigned int y,x; unsigned int height = offscreen_surface->rect().height(); unsigned int width = offscreen_surface->rect().width(); png_byte *img_row = new png_byte[width*4]; png_bytep row_pointers[1]; row_pointers[0] = &img_row[0]; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { color pix = offscreen_surface->read_pixel(x, y); pix = offscreen_surface->rgba_color(pix); //printf("%x ",pix); img_row[x*4] = (pix & 0xFF000000) >> 24; //red img_row[x*4+1] = (pix & 0x00FF0000) >> 16;//green img_row[x*4+2] = (pix & 0x0000FF00) >> 8;//blue img_row[x*4+3] = 255 - (pix && 0x000000FF);//alpha } png_write_rows(png_ptr, &row_pointers[0], 1); } //write end png_write_end(png_ptr, info_ptr); //free the pallate png_free(png_ptr, palette); palette=NULL; delete(img_row); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); #else throw image_write_exception("image::write_png() No support for writting PNG files compiled in!"); #endif }
static gsize photos_operation_png_guess_sizes_count (GeglBuffer *buffer, gint compression, gint bitdepth, gboolean background, gdouble zoom, gint src_x, gint src_y, gint width, gint height) { gint bpp; gint i; gint png_color_type; gchar format_string[16]; const Babl *format; const Babl *format_buffer; gsize ret_val = 0; gsize size; guchar *pixels = NULL; png_infop info_ptr = NULL; png_structp png_ptr = NULL; format_buffer = gegl_buffer_get_format (buffer); if (babl_format_has_alpha (format_buffer)) { if (babl_format_get_n_components (format_buffer) != 2) { png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; strcpy (format_string, "R'G'B'A "); } else { png_color_type = PNG_COLOR_TYPE_GRAY_ALPHA; strcpy (format_string, "Y'A "); } } else { if (babl_format_get_n_components (format_buffer) != 1) { png_color_type = PNG_COLOR_TYPE_RGB; strcpy (format_string, "R'G'B' "); } else { png_color_type = PNG_COLOR_TYPE_GRAY; strcpy (format_string, "Y' "); } } if (bitdepth == 16) strcat (format_string, "u16"); else strcat (format_string, "u8"); png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) goto out; info_ptr = png_create_info_struct (png_ptr); if (info_ptr == NULL) goto out; if (setjmp (png_jmpbuf (png_ptr))) { ret_val = 0; goto out; } if (compression >= 0) png_set_compression_level (png_ptr, compression); photos_png_init_count (png_ptr, &size); png_set_IHDR (png_ptr, info_ptr, width, height, bitdepth, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_DEFAULT); if (background) { png_color_16 white; if (png_color_type == PNG_COLOR_TYPE_RGB || png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { white.red = 0xff; white.blue = 0xff; white.green = 0xff; } else { white.gray = 0xff; } png_set_bKGD (png_ptr, info_ptr, &white); } png_write_info (png_ptr, info_ptr); #if BYTE_ORDER == LITTLE_ENDIAN if (bitdepth > 8) png_set_swap (png_ptr); #endif format = babl_format (format_string); bpp = babl_format_get_bytes_per_pixel (format); pixels = g_malloc0 (width * bpp); for (i = 0; i < height; i++) { GeglRectangle rect; rect.x = src_x; rect.y = src_y + i; rect.width = width; rect.height = 1; gegl_buffer_get (buffer, &rect, zoom, format, pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); png_write_rows (png_ptr, &pixels, 1); } png_write_end (png_ptr, info_ptr); ret_val = size; out: g_free (pixels); png_destroy_write_struct (&png_ptr, &info_ptr); return ret_val; }
static void do_png_write(struct png_writer * writer, void * buffer, int w, int h, int type) { TRACE(SYSTEM, "write a %dx%d image into a png stream\n", w, h); png_structp write_ptr; png_infop info_ptr; png_text texts[1]; /* if png handler is passed from writer, we needn't * create them again, however, we also needn't chain * them into cleanup */ if (writer->write_ptr == NULL) { /* if we create a writer_ptr, then we can only use * a new info_ptr */ writer->info_ptr = NULL; write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, png_error, png_warning); writer->write_ptr = write_ptr; } else { write_ptr = writer->write_ptr; writer->write_ptr_save = write_ptr; } if (write_ptr == NULL) { ERROR(SYSTEM, "libpng: create write_ptr error\n"); THROW(EXCEPTION_CONTINUE, "libpng error"); } /* We have to use setjmp here, because if we * neglect to set up our own setjmp(), libpng will * call abort(). */ if (setjmp(png_jmpbuf(write_ptr))) { ERROR(SYSTEM, "libpng: write error\n"); THROW(EXCEPTION_CONTINUE, "libpng: write error"); } if (writer->info_ptr == NULL) { info_ptr = png_create_info_struct(write_ptr); writer->info_ptr = info_ptr; } else { info_ptr = writer->info_ptr; } /* use our custom writer */ png_set_write_fn(write_ptr, writer->io_ptr, writer->write_fn, writer->flush_fn); /* write routine */ png_set_IHDR(write_ptr, info_ptr, w, h, 8, type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); texts[0].key = "Software"; texts[0].text = "yaavg"; texts[0].compression = PNG_TEXT_COMPRESSION_NONE; png_set_text(write_ptr, info_ptr, texts, 1); png_write_info(write_ptr, info_ptr); int n, row_size; switch (type) { case PNG_COLOR_TYPE_RGB: row_size = 3 * w; break; case PNG_COLOR_TYPE_RGBA: row_size = 4 * w; break; default: FATAL(SYSTEM, "png write format error: %d\n", type); THROW(FATAL, "png write format error"); } VERBOSE(SYSTEM, "png stream write start\n"); for (n = h - 1; n >= 0; n--) { uint8_t * prow = buffer + n * row_size; png_write_rows(write_ptr, &prow, 1); } png_write_end(write_ptr, info_ptr); cleanup_writer(writer); }
BOOL CScreenCapture::PngWriteRow(LPBYTE pRow) { png_bytep rows[1] = {pRow}; png_write_rows(m_png_ptr, (png_bytepp)&rows, 1); return TRUE; }
CONVERT_IMAGERESULT write_PNG_file (CONVERT_IMG_ARRAY p_rowarray,CONVERT_IMGCONTEXT *output,CONVERT_IMG_INFO *p_imageinfo,CONVERT_CALLBACKS p_callbacks) { int16 colors,i; int16 maxcolor=MAXCOLORS-1; colorhist_vector chv; pixval maxval=MAXCOLORS-1; colorhash_table cht=NULL; png_structp write_ptr; png_infop info_ptr;/* important!*/ png_bytep rowbuf=NULL; int32 y; int num_pass, pass; CONVERT_IMAGERESULT result=CONV_OK; if (!output||!p_imageinfo) { return CONVERR_INVALIDPARAMS; } /* Figure out the colormap. */ chv = ppm_computecolorhist( (pixel **)p_rowarray, p_imageinfo->m_image_width, p_imageinfo->m_image_height, MAXCOLORS, &colors ); if ( chv == (colorhist_vector) 0 )/*more than 256 colors*/ { result=quantize_colors(p_rowarray,p_imageinfo->m_image_width,p_imageinfo->m_image_height,&maxcolor,&colors,&chv); if (result!=CONV_OK) return result; if ( chv == (colorhist_vector) 0 ) return CONVERR_INVALIDCOLORMAP; } /* And make a hash table for fast lookup. */ cht = ppm_colorhisttocolorhash( chv, colors ); if (!cht) { XP_ASSERT(FALSE); return CONVERR_OUTOFMEMORY; } write_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (void *)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); info_ptr=png_create_info_struct(write_ptr); info_ptr->width=p_imageinfo->m_image_width; info_ptr->height=p_imageinfo->m_image_height; info_ptr->bit_depth=8; info_ptr->color_type=3; /*3= indexed color*/ info_ptr->compression_type=0; /* only option available*/ info_ptr->filter_type=0; /* only valid value for adaptive filtering*/ info_ptr->interlace_type=0;/*0= no interlacing*/ info_ptr->num_palette=colors; /*from quantize_colors*/ info_ptr->palette=XP_ALLOC(3*colors);/*remember to free this*/ info_ptr->valid|=PNG_INFO_PLTE; for (i=0;i<colors;i++) { info_ptr->palette[i].red = PPM_GETR( chv[i].color ); info_ptr->palette[i].green = PPM_GETG( chv[i].color ); info_ptr->palette[i].blue = PPM_GETB( chv[i].color ); } ppm_freecolorhist( chv ); if (!info_ptr->palette) { png_destroy_write_struct(&write_ptr, &info_ptr); } /*transparancy???*/ if (setjmp(write_ptr->jmpbuf)) { png_destroy_write_struct(&write_ptr, &info_ptr); return CONVERR_BADWRITE; } png_init_io(write_ptr, output->m_stream.m_file); png_write_info(write_ptr, info_ptr); num_pass = 1; /*we need to look up each RGB and change it to the index of the color table*/ rowbuf=(png_bytep) XP_ALLOC(p_imageinfo->m_image_width); if (!rowbuf) { png_destroy_write_struct(&write_ptr, &info_ptr); return CONVERR_BADWRITE; } for (pass = 0; pass < num_pass; pass++) { for (y = 0; y < p_imageinfo->m_image_height; y++) { fill_png_row(p_rowarray[y],rowbuf,p_imageinfo->m_image_width,cht); png_write_rows(write_ptr, &rowbuf, 1); } } XP_FREE(rowbuf); png_write_end(write_ptr, NULL); XP_FREE(info_ptr->palette); png_destroy_write_struct(&write_ptr, &info_ptr); fclose(output->m_stream.m_file); return CONV_OK; }
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; }
static gint export_png (GeglOperation *operation, GeglBuffer *input, const GeglRectangle *result, png_structp png, png_infop info, gint compression, gint bit_depth) { gint i, src_x, src_y; png_uint_32 width, height; guchar *pixels; png_color_16 white; int png_color_type; gchar format_string[16]; const Babl *format; src_x = result->x; src_y = result->y; width = result->width; height = result->height; { const Babl *babl = gegl_buffer_get_format (input); if (bit_depth != 16) bit_depth = 8; if (babl_format_has_alpha (babl)) if (babl_format_get_n_components (babl) != 2) { png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; strcpy (format_string, "R'G'B'A "); } else { png_color_type = PNG_COLOR_TYPE_GRAY_ALPHA; strcpy (format_string, "Y'A "); } else if (babl_format_get_n_components (babl) != 1) { png_color_type = PNG_COLOR_TYPE_RGB; strcpy (format_string, "R'G'B' "); } else { png_color_type = PNG_COLOR_TYPE_GRAY; strcpy (format_string, "Y' "); } } if (bit_depth == 16) strcat (format_string, "u16"); else strcat (format_string, "u8"); if (setjmp (png_jmpbuf (png))) return -1; png_set_compression_level (png, compression); png_set_IHDR (png, info, width, height, bit_depth, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_DEFAULT); if (png_color_type == PNG_COLOR_TYPE_RGB || png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { white.red = 0xff; white.blue = 0xff; white.green = 0xff; png_set_sRGB_gAMA_and_cHRM (png, info, PNG_sRGB_INTENT_RELATIVE); } else white.gray = 0xff; png_set_bKGD (png, info, &white); png_write_info (png, info); #if BYTE_ORDER == LITTLE_ENDIAN if (bit_depth > 8) png_set_swap (png); #endif format = babl_format (format_string); pixels = g_malloc0 (width * babl_format_get_bytes_per_pixel (format)); for (i=0; i< height; i++) { GeglRectangle rect; rect.x = src_x; rect.y = src_y+i; rect.width = width; rect.height = 1; gegl_buffer_get (input, &rect, 1.0, babl_format (format_string), pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); png_write_rows (png, &pixels, 1); } png_write_end (png, info); g_free (pixels); return 0; }
bool Image::writePNG(std::ostream &os, bool strip_alpha) const { png_structp png_ptr; png_infop info_ptr; int color_type; switch (channels) { case 4: color_type = strip_alpha ? PNG_COLOR_TYPE_RGB : PNG_COLOR_TYPE_RGB_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 1: color_type = PNG_COLOR_TYPE_GRAY; break; default: assert(0); goto no_png; } png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) goto no_png; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, NULL); goto no_png; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); goto no_png; } png_set_write_fn(png_ptr, &os, pngWriteCallback, NULL); png_set_IHDR(png_ptr, info_ptr, width, height, 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_set_compression_level(png_ptr, png_compression_level); png_write_info(png_ptr, info_ptr); if (channels == 4 && strip_alpha) { png_set_filler(png_ptr, 0, PNG_FILLER_AFTER); } switch (channelType) { case TYPE_UNORM8: for (const unsigned char *row = start(); row != end(); row += stride()) { png_write_rows(png_ptr, (png_bytepp) &row, 1); } break; case TYPE_FLOAT: png_bytep rowUnorm8 = new png_byte[width * channels]; for (const unsigned char *row = start(); row != end(); row += stride()) { const float *rowFloat = (const float *)row; for (unsigned x = 0, i = 0; x < width; ++x) { for (unsigned channel = 0; channel < channels; ++channel, ++i) { float c = rowFloat[i]; bool srgb = channels >= 3 && channel < 3; rowUnorm8[i] = srgb ? floatToSRGB(c) : floatToUnorm8(c); } } png_write_rows(png_ptr, (png_bytepp) &rowUnorm8, 1); } delete [] rowUnorm8; break; } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); return true; no_png: return false; }
UT_Error IE_ImpGraphic_BMP::Convert_BMP(UT_ByteBuf* pBB) { /* Reset error handling for libpng */ if (setjmp(png_jmpbuf(m_pPNG))) { png_destroy_write_struct(&m_pPNG, &m_pPNGInfo); return UT_ERROR; } png_write_info(m_pPNG,m_pPNGInfo); const UT_Byte* row_data; UT_sint32 row; UT_uint32 position; UT_uint32 row_width = m_iWidth * m_iBitsPerPlane / 8; while ((row_width & 3) != 0) row_width++; UT_Byte* row_transformed_data = new UT_Byte[row_width]; switch (m_iBitsPerPlane) { case 1: case 4: case 8: case 16: for (row=m_iHeight-1; row >= 0; row--) { /* Calculating the start of each row */ position=m_iOffset + row*row_width; row_data = reinterpret_cast<const unsigned char *>(pBB->getPointer(position)); png_write_rows(m_pPNG,const_cast<png_byte **>(reinterpret_cast<const png_byte **>(&row_data)),1); } break; case 24: case 48: for (row=m_iHeight-1; row >= 0; row--) { /* Calculating the start of each row */ position=m_iOffset + row*row_width; /* Transforming the b/r to r/b */ for (UT_sint32 i=0, col=0; i < m_iWidth; i++,col+=3) { row_transformed_data[col+0] = (UT_Byte)*pBB->getPointer(position+col+2); row_transformed_data[col+1] = (UT_Byte)*pBB->getPointer(position+col+1); row_transformed_data[col+2] = (UT_Byte)*pBB->getPointer(position+col+0); } png_write_rows(m_pPNG,&row_transformed_data,1); } break; case 32: case 64: for (row=m_iHeight-1; row >= 0; row--) { /* Calculating the start of each row */ position=m_iOffset + row*row_width; /* Transforming the b/r to r/b */ for (UT_sint32 i=0, col=0; i < m_iWidth; i++,col+=4) { row_transformed_data[col+0] = (UT_Byte)*pBB->getPointer(position+col+2); row_transformed_data[col+1] = (UT_Byte)*pBB->getPointer(position+col+1); row_transformed_data[col+2] = (UT_Byte)*pBB->getPointer(position+col+0); row_transformed_data[col+3] = (UT_Byte)*pBB->getPointer(position+col+3); } png_write_rows(m_pPNG,&row_transformed_data,1); } break; default: return UT_IE_BOGUSDOCUMENT; break; } delete [] row_transformed_data; png_write_end(m_pPNG,m_pPNGInfo); return UT_OK; }
cache Png_Create(int width, int height, int numpal, dPalette_t* pal, int bits, cache data, int lump, int* size) { int i = 0; int x = 0; int j = 0; cache image; cache out; cache* row_pointers; png_structp png_ptr; png_infop info_ptr; png_colorp palette; // setup png pointer png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0); if(png_ptr == NULL) { WGen_Complain("Png_Create: Failed getting png_ptr"); return NULL; } // setup info pointer info_ptr = png_create_info_struct(png_ptr); if(info_ptr == NULL) { png_destroy_write_struct(&png_ptr, NULL); WGen_Complain("Png_Create: Failed getting info_ptr"); return NULL; } // what does this do again? if(setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); WGen_Complain("Png_Create: Failed on setjmp"); return NULL; } // setup custom data writing procedure png_set_write_fn(png_ptr, NULL, Png_WriteData, NULL); // setup image png_set_IHDR( png_ptr, info_ptr, width, height, bits, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_DEFAULT); // setup palette palette = (png_colorp)Mem_Alloc((16 * numpal) * png_sizeof(png_color)); // copy dPalette_t data over to png_colorp for(x = 0, j = 0; x < numpal; x++) { for(i = 0; i < 16; i++) { palette[j].red = pal[j].r; palette[j].green = pal[j].g; palette[j].blue = pal[j].b; j++; } } i = 0; // add palette to png png_set_PLTE(png_ptr, info_ptr, palette, (16 * numpal)); // set transparent index if(palette[0].red == 0 && palette[0].green == 0 && palette[0].blue == 0) { char tmp[9]; strncpy(tmp, romWadFile.lump[lump].name, 8); tmp[0] -= (char)0x80; tmp[8] = 0; // Exempt these lumps if(strcmp(tmp, "FIRE") && /*strcmp(tmp, "USLEGAL") && strcmp(tmp, "TITLE") &&*/ strcmp(tmp, "EVIL") && /*strcmp(tmp, "IDCRED1") && strcmp(tmp, "WMSCRED1") &&*/ strcmp(tmp, "SPACE") && strcmp(tmp, "CLOUD") && strcmp(tmp, "FINAL")) png_set_tRNS(png_ptr, info_ptr, (png_bytep)&i, 1, NULL); } // add png info to data png_write_info(png_ptr, info_ptr); // add offset chunk if png is a sprite if(INSPRITELIST(lump)) { for(i = 0; i < spriteExCount; i++) { if(exSpriteLump[i].lumpRef == lump) { int offs[2]; offs[0] = WGen_Swap32(exSpriteLump[i].sprite.offsetx); offs[1] = WGen_Swap32(exSpriteLump[i].sprite.offsety); png_write_chunk(png_ptr, "grAb", (byte*)offs, 8); break; } } } // setup packing if needed png_set_packing(png_ptr); png_set_packswap(png_ptr); // copy data over image = data; row_pointers = (cache*)Mem_Alloc(sizeof(byte*) * height); for(i = 0; i < height; i++) { row_pointers[i] = (cache)Mem_Alloc(width); if(bits == 4) { for(j = 0; j < width; j += 2) { row_pointers[i][j] = *image & 0xf; row_pointers[i][j+1] = *image >> 4; image++; } } else { for(j = 0; j < width; j++) { row_pointers[i][j] = *image; image++; } } png_write_rows(png_ptr, &row_pointers[i], 1); }
bool writePng(const Bitmap &image, FILE *fp) { png_structp png_ptr; png_infop info_ptr; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png_ptr == NULL) { fclose(fp); return false; } /* Allocate/initialize the image information data. REQUIRED */ info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { fclose(fp); png_destroy_write_struct(&png_ptr, NULL); return false; } /* Set error handling. REQUIRED if you aren't supplying your own * error handling functions in the png_create_write_struct() call. */ if (setjmp(png_jmpbuf(png_ptr))) { /* If we get here, we had a problem writing the file */ fclose(fp); png_destroy_write_struct(&png_ptr, &info_ptr); return false; } /* Set up the output control if you are using standard C streams */ png_init_io(png_ptr, fp); png_set_IHDR(png_ptr, info_ptr, image.width(), image.height(), 1, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); /* Optional gamma chunk is strongly suggested if you have any guess * as to the correct gamma of the image. */ png_set_gAMA(png_ptr, info_ptr, 1.0); png_set_pHYs(png_ptr, info_ptr, image.xRes(), image.yRes(), image.resUnit()); /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); int width_bytes = (image.width() + 7) >> 3; png_bytep row = new png_byte[width_bytes]; for (int y = 0; y < image.height(); y++) { for (int x = 0; x < image.width(); x += 8) { uint8_t b = 0; uint8_t mask = 0x80; for (int xx = x; xx < x+8 && xx < image.width(); xx++) { if (image.get(xx, y)) { b |= mask; } mask >>= 1; } row[x >> 3] = ~b; } png_write_rows(png_ptr, &row, 1); } delete [] row; /* It is REQUIRED to call this to finish writing the rest of the file */ png_write_end(png_ptr, info_ptr); /* Clean up after the write, and free any memory allocated */ png_destroy_write_struct(&png_ptr, &info_ptr); /* Close the file */ fclose(fp); return true; }
bool KIPIWriteImage::write2PNG(const QString& destPath) { /* check this out for b/w support: http://lxr.kde.org/source/playground/graphics/krita-exp/kis_png_converter.cpp#607 */ QFile file(destPath); if (!file.open(QIODevice::ReadWrite)) { qDebug() << "Failed to open PNG file for writing" ; return false; } uchar* data = 0; int bitsDepth = d->sixteenBit ? 16 : 8; png_color_8 sig_bit; png_bytep row_ptr; png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_infop info_ptr = png_create_info_struct(png_ptr); png_set_write_fn(png_ptr, (void*)&file, kipi_png_write_fn, kipi_png_flush_fn); if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) // Intel png_set_bgr(png_ptr); else // PPC png_set_swap_alpha(png_ptr); if (d->hasAlpha) { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 8 * sizeof(uchar)]; else data = new uchar[d->width * 4 * sizeof(uchar)]; } else { png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); if (d->sixteenBit) data = new uchar[d->width * 6 * sizeof(uchar)]; else data = new uchar[d->width * 3 * sizeof(uchar)]; } sig_bit.red = bitsDepth; sig_bit.green = bitsDepth; sig_bit.blue = bitsDepth; sig_bit.alpha = bitsDepth; png_set_sBIT(png_ptr, info_ptr, &sig_bit); png_set_compression_level(png_ptr, 9); // Write Software info. QString libpngver(QLatin1String(PNG_HEADER_VERSION_STRING)); libpngver.replace(QLatin1Char('\n'), QLatin1Char(' ')); QByteArray softAscii = libpngver.toLatin1(); png_text text; text.key = (png_charp)"Software"; text.text = softAscii.data(); text.compression = PNG_TEXT_COMPRESSION_zTXt; png_set_text(png_ptr, info_ptr, &(text), 1); png_write_info(png_ptr, info_ptr); png_set_shift(png_ptr, &sig_bit); png_set_packing(png_ptr); uchar* ptr = (uchar*)d->data.data(); uint x, y, j; for (y = 0; y < d->height; ++y) { if (cancel()) { delete [] data; file.close(); png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); return false; } j = 0; for (x = 0; x < d->width*bytesDepth(); x+=bytesDepth()) { if (d->sixteenBit) { if (d->hasAlpha) { data[j++] = ptr[x+1]; // Blue data[j++] = ptr[ x ]; data[j++] = ptr[x+3]; // Green data[j++] = ptr[x+2]; data[j++] = ptr[x+5]; // Red data[j++] = ptr[x+4]; data[j++] = ptr[x+7]; // Alpha data[j++] = ptr[x+6]; } else { data[j++] = ptr[x+1]; // Blue data[j++] = ptr[ x ]; data[j++] = ptr[x+3]; // Green data[j++] = ptr[x+2]; data[j++] = ptr[x+5]; // Red data[j++] = ptr[x+4]; } } else { if (d->hasAlpha) { data[j++] = ptr[ x ]; // Blue data[j++] = ptr[x+1]; // Green data[j++] = ptr[x+2]; // Red data[j++] = ptr[x+3]; // Alpha } else { data[j++] = ptr[ x ]; // Blue data[j++] = ptr[x+1]; // Green data[j++] = ptr[x+2]; // Red } } } row_ptr = (png_bytep) data; png_write_rows(png_ptr, &row_ptr, 1); ptr += (d->width * bytesDepth()); } delete [] data; png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); file.close(); return true; }
bool GR_Win32Image::convertToBuffer(UT_ByteBuf** ppBB) const { /* The purpose of this routine is to convert our DIB (m_pDIB) into a PNG image, storing it in a ByteBuf and returning it to the caller. */ // Create our bytebuf UT_ByteBuf* pBB = new UT_ByteBuf(); png_structp png_ptr; png_infop info_ptr; // initialize some libpng stuff png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, (png_voidp)NULL, (png_error_ptr)NULL, (png_error_ptr)NULL); info_ptr = png_create_info_struct(png_ptr); // libpng will longjmp back to here if a fatal error occurs if (setjmp(png_jmpbuf(png_ptr))) { /* If we get here, we had a problem reading the file */ png_destroy_write_struct(&png_ptr, (png_infopp)NULL); *ppBB = NULL; return false; } // We want libpng to write to our ByteBuf, not stdio png_set_write_fn(png_ptr, (void *)pBB, _png_write, _png_flush); UT_uint32 iWidth = m_pDIB->bmiHeader.biWidth; UT_uint32 iHeight; /* DIBs are usually bottom-up (backwards, if you ask me), but sometimes they are top-down. */ bool bTopDown = false; if (m_pDIB->bmiHeader.biHeight < 0) { iHeight = -(m_pDIB->bmiHeader.biHeight); bTopDown = true; } else { iHeight = (m_pDIB->bmiHeader.biHeight); } png_set_IHDR(png_ptr, info_ptr, iWidth, iHeight, 8, // 8 bits per channel (24 bits) PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Write the file header information. REQUIRED */ png_write_info(png_ptr, info_ptr); /* The next big thing is writing out all of the pixels into the PNG file. We've got quite a bit of code here to handle various kinds of DIB images. */ UT_uint32 iSizeOfColorData = m_pDIB->bmiHeader.biClrUsed * sizeof(RGBQUAD); RGBQUAD* pColors = (RGBQUAD*) (((unsigned char*) m_pDIB) + m_pDIB->bmiHeader.biSize); UT_Byte* pBits = ((unsigned char*) m_pDIB) + m_pDIB->bmiHeader.biSize + iSizeOfColorData; UT_Byte* pData = (UT_Byte*) g_try_malloc(iWidth * iHeight * 3); UT_return_val_if_fail(pData, false); // TODO outofmem UT_uint32 iRow; UT_uint32 iCol; UT_Byte* pRow; UT_uint32 iBytesInRow; /* We can handle a DIB in either 4-bit, 8-bit, or 24-bit format. The way we do this is allocate a single 24-bit buffer, and regardless of what the format of the DIB is, we convert it to our 24-bit buffer. Below, we will copy our 24-bit buffer into the PNG file. */ switch (m_pDIB->bmiHeader.biBitCount) { case 4: { iBytesInRow = iWidth / 2; if (iWidth % 2) { iBytesInRow++; } if (iBytesInRow % 4) { iBytesInRow += (4 - (iBytesInRow % 4)); } for (iRow = 0; iRow<iHeight; iRow++) { if (bTopDown) { pRow = pBits + iRow * iBytesInRow; } else { pRow = pBits + (iHeight - iRow - 1) * iBytesInRow; } for (iCol=0; iCol<iWidth; iCol++) { UT_Byte byt = pRow[iCol / 2]; UT_Byte pixel; if (iCol % 2) { pixel = byt & 0xf; } else { pixel = byt >> 4; } UT_ASSERT(pixel < m_pDIB->bmiHeader.biClrUsed); pData[(iRow*iWidth + iCol)*3 + 0] = pColors[pixel].rgbRed; pData[(iRow*iWidth + iCol)*3 + 1] = pColors[pixel].rgbGreen; pData[(iRow*iWidth + iCol)*3 + 2] = pColors[pixel].rgbBlue; } } break; } case 8: { iBytesInRow = iWidth; if (iBytesInRow % 4) { iBytesInRow += (4 - (iBytesInRow % 4)); } for (iRow = 0; iRow<iHeight; iRow++) { if (bTopDown) { pRow = pBits + iRow * iBytesInRow; } else { pRow = pBits + (iHeight - iRow - 1) * iBytesInRow; } for (iCol=0; iCol<iWidth; iCol++) { UT_Byte pixel = pRow[iCol]; UT_ASSERT(pixel < m_pDIB->bmiHeader.biClrUsed); pData[(iRow*iWidth + iCol)*3 + 0] = pColors[pixel].rgbRed; pData[(iRow*iWidth + iCol)*3 + 1] = pColors[pixel].rgbGreen; pData[(iRow*iWidth + iCol)*3 + 2] = pColors[pixel].rgbBlue; } } break; } case 24: { iBytesInRow = iWidth * 3; if (iBytesInRow % 4) { iBytesInRow += (4 - (iBytesInRow % 4)); } for (iRow = 0; iRow<iHeight; iRow++) { if (bTopDown) { pRow = pBits + iRow * iBytesInRow; } else { pRow = pBits + (iHeight - iRow - 1) * iBytesInRow; } for (iCol=0; iCol<iWidth; iCol++) { pData[(iRow*iWidth + iCol)*3 + 0] = pRow[iCol*3 + 2]; pData[(iRow*iWidth + iCol)*3 + 1] = pRow[iCol*3 + 1]; pData[(iRow*iWidth + iCol)*3 + 2] = pRow[iCol*3 + 0]; } } break; } default: // there are DIB formats we do not support. UT_ASSERT_HARMLESS(UT_TODO); break; } /* Now that we have converted the image to a normalized 24-bit representation, we can save it out to the PNG file. */ for (UT_uint32 i=0; i<iHeight; i++) { UT_Byte *pRow = pData + i * iWidth * 3; png_write_rows(png_ptr, &pRow, 1); } /* We then g_free our 24-bit buffer. */ g_free(pData); /* Wrap things up with libpng */ png_write_end(png_ptr, info_ptr); /* clean up after the write, and g_free any memory allocated */ png_destroy_write_struct(&png_ptr, (png_infopp)NULL); // And pass the ByteBuf back to our caller *ppBB = pBB; return true; }
bool wxPNGHandler::SaveFile( wxImage *image, wxOutputStream& stream, bool verbose ) { wxPNGInfoStruct wxinfo; wxinfo.verbose = verbose; wxinfo.stream.out = &stream; png_structp png_ptr = png_create_write_struct ( PNG_LIBPNG_VER_STRING, NULL, wx_PNG_error, wx_PNG_warning ); if (!png_ptr) { if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } png_infop info_ptr = png_create_info_struct(png_ptr); if (info_ptr == NULL) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } if (setjmp(wxinfo.jmpbuf)) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); if (verbose) { wxLogError(_("Couldn't save PNG image.")); } return false; } // NB: please see the comment near wxPNGInfoStruct declaration for // explanation why this line is mandatory png_set_write_fn( png_ptr, &wxinfo, wx_PNG_stream_writer, NULL); const int iHeight = image->GetHeight(); const int iWidth = image->GetWidth(); const bool bHasPngFormatOption = image->HasOption(wxIMAGE_OPTION_PNG_FORMAT); int iColorType = bHasPngFormatOption ? image->GetOptionInt(wxIMAGE_OPTION_PNG_FORMAT) : wxPNG_TYPE_COLOUR; bool bHasAlpha = image->HasAlpha(); bool bHasMask = image->HasMask(); bool bUsePalette = iColorType == wxPNG_TYPE_PALETTE #if wxUSE_PALETTE || (!bHasPngFormatOption && image->HasPalette() ) #endif ; png_color_8 mask = { 0, 0, 0, 0, 0 }; if (bHasMask) { mask.red = image->GetMaskRed(); mask.green = image->GetMaskGreen(); mask.blue = image->GetMaskBlue(); } PaletteMap palette; if (bUsePalette) { png_color png_rgb [PNG_MAX_PALETTE_LENGTH]; png_byte png_trans[PNG_MAX_PALETTE_LENGTH]; const unsigned char *pColors = image->GetData(); const unsigned char* pAlpha = image->GetAlpha(); if (bHasMask && !pAlpha) { // Mask must be first PaletteAdd(&palette, mask); } for (int y = 0; y < iHeight; y++) { for (int x = 0; x < iWidth; x++) { png_color_8 rgba; rgba.red = *pColors++; rgba.green = *pColors++; rgba.blue = *pColors++; rgba.gray = 0; rgba.alpha = (pAlpha && !bHasMask) ? *pAlpha++ : 0; // save in our palette long index = PaletteAdd(&palette, rgba); if (index < PNG_MAX_PALETTE_LENGTH) { // save in libpng's palette png_rgb[index].red = rgba.red; png_rgb[index].green = rgba.green; png_rgb[index].blue = rgba.blue; png_trans[index] = rgba.alpha; } else { bUsePalette = false; break; } } } if (bUsePalette) { png_set_PLTE(png_ptr, info_ptr, png_rgb, palette.size()); if (bHasMask && !pAlpha) { wxASSERT(PaletteFind(palette, mask) == 0); png_trans[0] = 0; png_set_tRNS(png_ptr, info_ptr, png_trans, 1, NULL); } else if (pAlpha && !bHasMask) { png_set_tRNS(png_ptr, info_ptr, png_trans, palette.size(), NULL); } } } /* If saving palettised was requested but it was decided we can't use a palette then reset the colour type to RGB. */ if (!bUsePalette && iColorType == wxPNG_TYPE_PALETTE) { iColorType = wxPNG_TYPE_COLOUR; } bool bUseAlpha = !bUsePalette && (bHasAlpha || bHasMask); int iPngColorType; if (bUsePalette) { iPngColorType = PNG_COLOR_TYPE_PALETTE; iColorType = wxPNG_TYPE_PALETTE; } else if ( iColorType==wxPNG_TYPE_COLOUR ) { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; } else { iPngColorType = bUseAlpha ? PNG_COLOR_TYPE_GRAY_ALPHA : PNG_COLOR_TYPE_GRAY; } if (image->HasOption(wxIMAGE_OPTION_PNG_FILTER)) png_set_filter( png_ptr, PNG_FILTER_TYPE_BASE, image->GetOptionInt(wxIMAGE_OPTION_PNG_FILTER) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL)) png_set_compression_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL)) png_set_compression_mem_level( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_MEM_LEVEL) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY)) png_set_compression_strategy( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_STRATEGY) ); if (image->HasOption(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE)) png_set_compression_buffer_size( png_ptr, image->GetOptionInt(wxIMAGE_OPTION_PNG_COMPRESSION_BUFFER_SIZE) ); int iBitDepth = !bUsePalette && image->HasOption(wxIMAGE_OPTION_PNG_BITDEPTH) ? image->GetOptionInt(wxIMAGE_OPTION_PNG_BITDEPTH) : 8; png_set_IHDR( png_ptr, info_ptr, image->GetWidth(), image->GetHeight(), iBitDepth, iPngColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); int iElements; png_color_8 sig_bit; if ( iPngColorType & PNG_COLOR_MASK_COLOR ) { sig_bit.red = sig_bit.green = sig_bit.blue = (png_byte)iBitDepth; iElements = 3; } else // grey { sig_bit.gray = (png_byte)iBitDepth; iElements = 1; } if ( bUseAlpha ) { sig_bit.alpha = (png_byte)iBitDepth; iElements++; } if ( iBitDepth == 16 ) iElements *= 2; // save the image resolution if we have it int resX, resY; switch ( GetResolutionFromOptions(*image, &resX, &resY) ) { case wxIMAGE_RESOLUTION_INCHES: { const double INCHES_IN_METER = 10000.0 / 254; resX = int(resX * INCHES_IN_METER); resY = int(resY * INCHES_IN_METER); } break; case wxIMAGE_RESOLUTION_CM: resX *= 100; resY *= 100; break; case wxIMAGE_RESOLUTION_NONE: break; default: wxFAIL_MSG( wxT("unsupported image resolution units") ); } if ( resX && resY ) png_set_pHYs( png_ptr, info_ptr, resX, resY, PNG_RESOLUTION_METER ); png_set_sBIT( png_ptr, info_ptr, &sig_bit ); png_write_info( png_ptr, info_ptr ); png_set_shift( png_ptr, &sig_bit ); png_set_packing( png_ptr ); unsigned char * data = (unsigned char *)malloc( image->GetWidth() * iElements ); if ( !data ) { png_destroy_write_struct( &png_ptr, (png_infopp)NULL ); return false; } const unsigned char * pAlpha = (const unsigned char *)(bHasAlpha ? image->GetAlpha() : NULL); const unsigned char *pColors = image->GetData(); for (int y = 0; y != iHeight; ++y) { unsigned char *pData = data; for (int x = 0; x != iWidth; x++) { png_color_8 clr; clr.red = *pColors++; clr.green = *pColors++; clr.blue = *pColors++; clr.gray = 0; clr.alpha = (bUsePalette && pAlpha) ? *pAlpha++ : 0; // use with wxPNG_TYPE_PALETTE only switch ( iColorType ) { default: wxFAIL_MSG( wxT("unknown wxPNG_TYPE_XXX") ); // fall through case wxPNG_TYPE_COLOUR: *pData++ = clr.red; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = clr.green; if ( iBitDepth == 16 ) *pData++ = 0; *pData++ = clr.blue; if ( iBitDepth == 16 ) *pData++ = 0; break; case wxPNG_TYPE_GREY: { // where do these coefficients come from? maybe we // should have image options for them as well? unsigned uiColor = (unsigned) (76.544*(unsigned)clr.red + 150.272*(unsigned)clr.green + 36.864*(unsigned)clr.blue); *pData++ = (unsigned char)((uiColor >> 8) & 0xFF); if ( iBitDepth == 16 ) *pData++ = (unsigned char)(uiColor & 0xFF); } break; case wxPNG_TYPE_GREY_RED: *pData++ = clr.red; if ( iBitDepth == 16 ) *pData++ = 0; break; case wxPNG_TYPE_PALETTE: *pData++ = (unsigned char) PaletteFind(palette, clr); break; } if ( bUseAlpha ) { unsigned char uchAlpha = 255; if ( bHasAlpha ) uchAlpha = *pAlpha++; if ( bHasMask ) { if ( (clr.red == mask.red) && (clr.green == mask.green) && (clr.blue == mask.blue) ) uchAlpha = 0; } *pData++ = uchAlpha; if ( iBitDepth == 16 ) *pData++ = 0; } } png_bytep row_ptr = data; png_write_rows( png_ptr, &row_ptr, 1 ); } free(data); png_write_end( png_ptr, info_ptr ); png_destroy_write_struct( &png_ptr, (png_infopp)&info_ptr ); return true; }
/* This routine is used for all formats. */ static int png_print_page(gx_device_printer * pdev, FILE * file) { gs_memory_t *mem = pdev->memory; int raster = gdev_prn_raster(pdev); /* PNG structures */ byte *row = gs_alloc_bytes(mem, raster, "png raster buffer"); png_struct *png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); png_info *info_ptr = png_create_info_struct(png_ptr); int height = pdev->height; int depth = pdev->color_info.depth; int y; int code; /* return code */ char software_key[80]; char software_text[256]; png_text text_png; if (row == 0 || png_ptr == 0 || info_ptr == 0) { code = gs_note_error(gs_error_VMerror); goto done; } /* set error handling */ if (setjmp(png_ptr->jmpbuf)) { /* If we get here, we had a problem reading the file */ code = gs_note_error(gs_error_VMerror); goto done; } code = 0; /* for normal path */ /* set up the output control */ png_init_io(png_ptr, file); /* set the file information here */ info_ptr->width = pdev->width; info_ptr->height = pdev->height; /* resolution is in pixels per meter vs. dpi */ info_ptr->x_pixels_per_unit = (png_uint_32) (pdev->HWResolution[0] * (100.0 / 2.54)); info_ptr->y_pixels_per_unit = (png_uint_32) (pdev->HWResolution[1] * (100.0 / 2.54)); info_ptr->phys_unit_type = PNG_RESOLUTION_METER; info_ptr->valid |= PNG_INFO_pHYs; switch (depth) { case 32: info_ptr->bit_depth = 8; info_ptr->color_type = PNG_COLOR_TYPE_RGB_ALPHA; png_set_invert_alpha(png_ptr); { gx_device_pngalpha *ppdev = (gx_device_pngalpha *)pdev; png_color_16 background; background.index = 0; background.red = (ppdev->background >> 16) & 0xff; background.green = (ppdev->background >> 8) & 0xff; background.blue = (ppdev->background) & 0xff; background.gray = 0; png_set_bKGD(png_ptr, info_ptr, &background); } break; case 48: info_ptr->bit_depth = 16; info_ptr->color_type = PNG_COLOR_TYPE_RGB; #if defined(ARCH_IS_BIG_ENDIAN) && (!ARCH_IS_BIG_ENDIAN) png_set_swap(png_ptr); #endif break; case 24: info_ptr->bit_depth = 8; info_ptr->color_type = PNG_COLOR_TYPE_RGB; break; case 8: info_ptr->bit_depth = 8; if (gx_device_has_color(pdev)) info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; else info_ptr->color_type = PNG_COLOR_TYPE_GRAY; break; case 4: info_ptr->bit_depth = 4; info_ptr->color_type = PNG_COLOR_TYPE_PALETTE; break; case 1: info_ptr->bit_depth = 1; info_ptr->color_type = PNG_COLOR_TYPE_GRAY; /* invert monocrome pixels */ png_set_invert_mono(png_ptr); break; } /* set the palette if there is one */ if (info_ptr->color_type == PNG_COLOR_TYPE_PALETTE) { int i; int num_colors = 1 << depth; gx_color_value rgb[3]; info_ptr->palette = (void *)gs_alloc_bytes(mem, 256 * sizeof(png_color), "png palette"); if (info_ptr->palette == 0) { code = gs_note_error(gs_error_VMerror); goto done; } info_ptr->num_palette = num_colors; info_ptr->valid |= PNG_INFO_PLTE; for (i = 0; i < num_colors; i++) { (*dev_proc(pdev, map_color_rgb)) ((gx_device *) pdev, (gx_color_index) i, rgb); info_ptr->palette[i].red = gx_color_value_to_byte(rgb[0]); info_ptr->palette[i].green = gx_color_value_to_byte(rgb[1]); info_ptr->palette[i].blue = gx_color_value_to_byte(rgb[2]); } } /* add comment */ strncpy(software_key, "Software", sizeof(software_key)); sprintf(software_text, "%s %d.%02d", gs_product, (int)(gs_revision / 100), (int)(gs_revision % 100)); text_png.compression = -1; /* uncompressed */ text_png.key = software_key; text_png.text = software_text; text_png.text_length = strlen(software_text); info_ptr->text = &text_png; info_ptr->num_text = 1; /* write the file information */ png_write_info(png_ptr, info_ptr); /* don't write the comments twice */ info_ptr->num_text = 0; info_ptr->text = NULL; /* Write the contents of the image. */ for (y = 0; y < height; y++) { gdev_prn_copy_scan_lines(pdev, y, row, raster); png_write_rows(png_ptr, &row, 1); } /* write the rest of the file */ png_write_end(png_ptr, info_ptr); /* if you alloced the palette, free it here */ gs_free_object(mem, info_ptr->palette, "png palette"); done: /* free the structures */ png_destroy_write_struct(&png_ptr, &info_ptr); gs_free_object(mem, row, "png raster buffer"); return code; }
bool Image::writePNG(const char *filename) const { FILE *fp; png_structp png_ptr; png_infop info_ptr; fp = fopen(filename, "wb"); if (!fp) goto no_fp; png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (!png_ptr) goto no_png; info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { png_destroy_write_struct(&png_ptr, NULL); goto no_png; } if (setjmp(png_jmpbuf(png_ptr))) { png_destroy_write_struct(&png_ptr, &info_ptr); goto no_png; } png_init_io(png_ptr, fp); int color_type; switch (channels) { case 4: color_type = PNG_COLOR_TYPE_RGB_ALPHA; break; case 3: color_type = PNG_COLOR_TYPE_RGB; break; case 2: color_type = PNG_COLOR_TYPE_GRAY_ALPHA; break; case 1: color_type = PNG_COLOR_TYPE_GRAY; break; default: assert(0); return false; } png_set_IHDR(png_ptr, info_ptr, width, height, 8, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_set_compression_level(png_ptr, Z_DEFAULT_COMPRESSION); png_write_info(png_ptr, info_ptr); if (!flipped) { for (unsigned y = 0; y < height; ++y) { png_bytep row = (png_bytep)(pixels + y*width*channels); png_write_rows(png_ptr, &row, 1); } } else { unsigned y = height; while (y--) { png_bytep row = (png_bytep)(pixels + y*width*channels); png_write_rows(png_ptr, &row, 1); } } png_write_end(png_ptr, info_ptr); png_destroy_write_struct(&png_ptr, &info_ptr); fclose(fp); return true; no_png: fclose(fp); no_fp: return false; }
static gboolean real_save_png (GdkPixbuf *pixbuf, gchar **keys, gchar **values, GError **error, gboolean to_callback, FILE *f, GdkPixbufSaveFunc save_func, gpointer user_data) { png_structp png_ptr = NULL; png_infop info_ptr; png_textp text_ptr = NULL; guchar *ptr; guchar *pixels; int y; int i; png_bytep row_ptr; png_color_8 sig_bit; int w, h, rowstride; int has_alpha; int bpc; int num_keys; int compression = -1; gboolean success = TRUE; guchar *icc_profile = NULL; gsize icc_profile_size = 0; SaveToFunctionIoPtr to_callback_ioptr; num_keys = 0; if (keys && *keys) { gchar **kiter = keys; gchar **viter = values; while (*kiter) { if (strncmp (*kiter, "tEXt::", 6) == 0) { gchar *key = *kiter + 6; int len = strlen (key); if (len < 1 || len > 79) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("Keys for PNG text chunks must have at least 1 and at most 79 characters.")); success = FALSE; goto cleanup; } for (i = 0; i < len; i++) { if ((guchar) key[i] > 127) { g_set_error_literal (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("Keys for PNG text chunks must be ASCII characters.")); success = FALSE; goto cleanup; } } num_keys++; } else if (strcmp (*kiter, "icc-profile") == 0) { /* decode from base64 */ icc_profile = g_base64_decode (*viter, &icc_profile_size); if (icc_profile_size < 127) { /* This is a user-visible error */ g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("Color profile has invalid length %d."), (gint)icc_profile_size); success = FALSE; goto cleanup; } } else if (strcmp (*kiter, "compression") == 0) { char *endptr = NULL; compression = strtol (*viter, &endptr, 10); if (endptr == *viter) { g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("PNG compression level must be a value between 0 and 9; value '%s' could not be parsed."), *viter); success = FALSE; goto cleanup; } if (compression < 0 || compression > 9) { /* This is a user-visible error; * lets people skip the range-checking * in their app. */ g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("PNG compression level must be a value between 0 and 9; value '%d' is not allowed."), compression); success = FALSE; goto cleanup; } } else { g_warning ("Unrecognized parameter (%s) passed to PNG saver.", *kiter); } ++kiter; ++viter; } } if (num_keys > 0) { gchar **kiter = keys; gchar **viter = values; text_ptr = g_new0 (png_text, num_keys); for (i = 0; i < num_keys; i++) { if (strncmp (*kiter, "tEXt::", 6) != 0) { kiter++; viter++; } text_ptr[i].compression = PNG_TEXT_COMPRESSION_NONE; text_ptr[i].key = *kiter + 6; text_ptr[i].text = g_convert (*viter, -1, "ISO-8859-1", "UTF-8", NULL, &text_ptr[i].text_length, NULL); #ifdef PNG_iTXt_SUPPORTED if (!text_ptr[i].text) { text_ptr[i].compression = PNG_ITXT_COMPRESSION_NONE; text_ptr[i].text = g_strdup (*viter); text_ptr[i].text_length = 0; text_ptr[i].itxt_length = strlen (text_ptr[i].text); text_ptr[i].lang = NULL; text_ptr[i].lang_key = NULL; } #endif if (!text_ptr[i].text) { gint j; g_set_error (error, GDK_PIXBUF_ERROR, GDK_PIXBUF_ERROR_BAD_OPTION, _("Value for PNG text chunk %s cannot be converted to ISO-8859-1 encoding."), *kiter + 6); for (j = 0; j < i; j++) g_free (text_ptr[j].text); g_free (text_ptr); return FALSE; } kiter++; viter++; } } bpc = gdk_pixbuf_get_bits_per_sample (pixbuf); w = gdk_pixbuf_get_width (pixbuf); h = gdk_pixbuf_get_height (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); pixels = gdk_pixbuf_get_pixels (pixbuf); png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, error, png_simple_error_callback, png_simple_warning_callback); if (png_ptr == NULL) { success = FALSE; goto cleanup; } info_ptr = png_create_info_struct (png_ptr); if (info_ptr == NULL) { success = FALSE; goto cleanup; } if (setjmp (png_jmpbuf(png_ptr))) { success = FALSE; goto cleanup; } if (num_keys > 0) { png_set_text (png_ptr, info_ptr, text_ptr, num_keys); } if (to_callback) { to_callback_ioptr.save_func = save_func; to_callback_ioptr.user_data = user_data; to_callback_ioptr.error = error; png_set_write_fn (png_ptr, &to_callback_ioptr, png_save_to_callback_write_func, png_save_to_callback_flush_func); } else { png_init_io (png_ptr, f); } if (compression >= 0) png_set_compression_level (png_ptr, compression); #if defined(PNG_iCCP_SUPPORTED) /* the proper ICC profile title is encoded in the profile */ if (icc_profile != NULL) { png_set_iCCP (png_ptr, info_ptr, "ICC profile", PNG_COMPRESSION_TYPE_BASE, (png_bytep) icc_profile, icc_profile_size); } #endif if (has_alpha) { png_set_IHDR (png_ptr, info_ptr, w, h, bpc, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } else { png_set_IHDR (png_ptr, info_ptr, w, h, bpc, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); } sig_bit.red = bpc; sig_bit.green = bpc; sig_bit.blue = bpc; sig_bit.alpha = bpc; png_set_sBIT (png_ptr, info_ptr, &sig_bit); png_write_info (png_ptr, info_ptr); png_set_shift (png_ptr, &sig_bit); png_set_packing (png_ptr); ptr = pixels; for (y = 0; y < h; y++) { row_ptr = (png_bytep)ptr; png_write_rows (png_ptr, &row_ptr, 1); ptr += rowstride; } png_write_end (png_ptr, info_ptr); cleanup: if (png_ptr != NULL) png_destroy_write_struct (&png_ptr, &info_ptr); g_free (icc_profile); if (text_ptr != NULL) { for (i = 0; i < num_keys; i++) g_free (text_ptr[i].text); g_free (text_ptr); } return success; }
gint gegl_buffer_export_png (GeglBuffer *gegl_buffer, const gchar *path, gint compression, gint bd, gint src_x, gint src_y, gint width, gint height) { FILE *fp; gint i; png_struct *png; png_info *info; guchar *pixels; png_color_16 white; int png_color_type; gchar format_string[16]; const Babl *format; gint bit_depth = 8; if (!strcmp (path, "-")) { fp = stdout; } else { fp = fopen (path, "wb"); } if (!fp) { return -1; } { const Babl *babl = gegl_buffer_get_format (gegl_buffer); if (bd == 16) bit_depth = 16; else bit_depth = 8; if (babl_format_has_alpha (babl)) if (babl_format_get_n_components (babl) != 2) { png_color_type = PNG_COLOR_TYPE_RGB_ALPHA; strcpy (format_string, "R'G'B'A "); } else { png_color_type = PNG_COLOR_TYPE_GRAY_ALPHA; strcpy (format_string, "Y'A "); } else if (babl_format_get_n_components (babl) != 1) { png_color_type = PNG_COLOR_TYPE_RGB; strcpy (format_string, "R'G'B' "); } else { png_color_type = PNG_COLOR_TYPE_GRAY; strcpy (format_string, "Y' "); } } if (bit_depth == 16) strcat (format_string, "u16"); else strcat (format_string, "u8"); png = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (png == NULL) { if (stdout != fp) fclose (fp); return -1; } info = png_create_info_struct (png); if (setjmp (png_jmpbuf (png))) { if (stdout != fp) fclose (fp); return -1; } png_set_compression_level (png, compression); png_init_io (png, fp); png_set_IHDR (png, info, width, height, bit_depth, png_color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_DEFAULT); if (png_color_type == PNG_COLOR_TYPE_RGB || png_color_type == PNG_COLOR_TYPE_RGB_ALPHA) { white.red = 0xff; white.blue = 0xff; white.green = 0xff; } else white.gray = 0xff; png_set_bKGD (png, info, &white); png_write_info (png, info); #if BYTE_ORDER == LITTLE_ENDIAN if (bit_depth > 8) png_set_swap (png); #endif format = babl_format (format_string); pixels = g_malloc0 (width * babl_format_get_bytes_per_pixel (format)); for (i=0; i< height; i++) { GeglRectangle rect; rect.x = src_x; rect.y = src_y+i; rect.width = width; rect.height = 1; gegl_buffer_get (gegl_buffer, &rect, 1.0, babl_format (format_string), pixels, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE); png_write_rows (png, &pixels, 1); } png_write_end (png, info); png_destroy_write_struct (&png, &info); g_free (pixels); if (stdout != fp) fclose (fp); return 0; }
/* WritePNG - writes the contents of a BMGImageStruct to a JPEG file. Inputs: filename - the name of the file to be opened img - the BMGImageStruct containing the image data Returns: 0 - if the file could not be written or a resource error occurred 1 - if the file was written Comments: 16-BPP BMG Images are converted to 24-BPP images Limitations: Color Type is limited to PNG_COLOR_TYPE_GRAY, PNG_COLOR_TYPE_RGB_ALPHA, PNG_COLOR_TYPE_RGB, & PNG_COLOR_TYPE_PALETTE; */ BMGError WritePNG( const char *filename, struct BMGImageStruct img ) { jmp_buf err_jmp; int error; int BitDepth; int ColorType; png_structp png_ptr = NULL; png_infop info_ptr = NULL; png_colorp PNGPalette = NULL; int GrayScale; unsigned char *bits, *p, *q; unsigned char **rows = NULL; int NumColors = 0; int DIBScanWidth; int HasPalette; FILE *outfile = NULL; int i; BMGError tmp; /* error handler */ error = setjmp( err_jmp ); if ( error != 0 ) { if ( png_ptr != NULL ) png_destroy_write_struct( &png_ptr, NULL ); if ( rows ) { if ( rows[0] ) { free( rows[0] ); } free( rows ); } if ( PNGPalette ) free( PNGPalette ); if ( outfile) fclose( outfile ); return (BMGError)error; } /* open the file */ if ((outfile = fopen(filename, "wb")) == NULL) longjmp( err_jmp, (int)errFileOpen ); /* 16 BPP DIBS do not have palettes. libPNG expects 16 BPP images to have a palette. To correct this situation we must convert 16 BPP images to 24 BPP images before saving the data to the file */ if ( img.bits_per_pixel == 16 ) { tmp = Convert16to24( &img ); if ( tmp != BMG_OK ) longjmp( err_jmp, (int)tmp ); } HasPalette = img.bits_per_pixel <= 8; if ( HasPalette ) { NumColors = img.palette_size; /* if this is a grayscale image then set the flag and delete the palette*/ i = 0; bits = img.palette; while ( i < NumColors && bits[0] == bits[1] && bits[0] == bits[2] ) { i++; bits += img.bytes_per_palette_entry; } GrayScale = i == NumColors; } else GrayScale = 0; /* dimensions */ DIBScanWidth = ( img.width * img.bits_per_pixel + 7 ) / 8; /* create the png pointer */ png_ptr = png_create_write_struct( PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if ( !png_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* create the info pointer */ info_ptr = png_create_info_struct( png_ptr ); if ( !info_ptr ) longjmp( err_jmp, (int)errMemoryAllocation ); /* bamboozle the png error handler */ /* error will always == 1 which equals errLib */ // error = png_setjmp(png_ptr); error = setjmp( png_jmpbuf( png_ptr ) ); if ( error > 0 ) longjmp( err_jmp, error ); /* setup the output control */ png_init_io( png_ptr, outfile ); /* prepare variables needed to create PNG header */ BitDepth = img.bits_per_pixel < 8 ? img.bits_per_pixel : 8; /* determine color type */ if ( GrayScale ) ColorType = PNG_COLOR_TYPE_GRAY; else if ( img.bits_per_pixel == 32 ) ColorType = PNG_COLOR_TYPE_RGB_ALPHA; else if ( img.bits_per_pixel == 24 ) ColorType = PNG_COLOR_TYPE_RGB; else ColorType = PNG_COLOR_TYPE_PALETTE; /* create the PNG header */ png_set_IHDR( png_ptr, info_ptr, img.width, img.height, BitDepth, ColorType, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE ); /* store the palette information if there is any */ if ( img.palette != NULL && !GrayScale ) { PNGPalette = (png_colorp)png_malloc( png_ptr, NumColors*sizeof(png_color)); if ( PNGPalette ) { bits = img.palette; for ( i = 0; i < NumColors; i++, bits += img.bytes_per_palette_entry ) { PNGPalette[i].red = bits[2]; PNGPalette[i].green = bits[1]; PNGPalette[i].blue = bits[0]; } png_set_PLTE( png_ptr, info_ptr, PNGPalette, NumColors ); } else longjmp( err_jmp, (int)errMemoryAllocation ); } /* write the file header information */ png_write_info( png_ptr, info_ptr ); /* create array to store data in */ rows = (unsigned char **)malloc(sizeof(unsigned char*)); if ( !rows ) longjmp( err_jmp, (int)errMemoryAllocation ); rows[0] = (unsigned char *)malloc( DIBScanWidth * sizeof(unsigned char)); if ( !rows[0] ) longjmp( err_jmp, (int)errMemoryAllocation ); /* point to the bottom row of the DIB data. DIBs are stored bottom-to-top, PNGs are stored top-to-bottom. */ bits = img.bits + (img.height - 1) * img.scan_width; /* store bits */ for ( i = 0; i < (int)img.height; i++ ) { switch ( img.bits_per_pixel ) { case 1: case 4: case 8: memcpy( (void *)rows[0], (void *)bits, DIBScanWidth ); break; case 24: q = bits; for ( p = rows[0]; p < rows[0] + DIBScanWidth; p += 3, q += 3 ) { p[0] = q[2]; p[1] = q[1]; p[2] = q[0]; } break; case 32: q = bits; for ( p = rows[0]; p < rows[0] + DIBScanWidth; p += 4, q += 4 ) { p[3] = q[3]; p[0] = q[2]; p[1] = q[1]; p[2] = q[0]; } break; } png_write_rows( png_ptr, rows, 1 ); bits -= img.scan_width; } /* finish writing the rest of the file */ png_write_end( png_ptr, info_ptr ); /* clean up and exit */ if ( PNGPalette ) free( PNGPalette ); free( rows[0] ); free( rows ); png_destroy_write_struct( &png_ptr, NULL ); fclose( outfile ); return BMG_OK; }
static gboolean _cairo_surface_write_as_png (cairo_surface_t *image, char **buffer, gsize *buffer_size, char **keys, char **values, GError **error) { int compression_level; int width, height; gboolean alpha; guchar *pixels, *ptr, *buf; int rowstride; CairoPngData *cairo_png_data; png_color_8 sig_bit; int bpp; int row; compression_level = 6; if (keys && *keys) { char **kiter = keys; char **viter = values; while (*kiter) { if (strcmp (*kiter, "compression") == 0) { if (*viter == NULL) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Must specify a compression level"); return FALSE; } compression_level = atoi (*viter); if (compression_level < 0 || compression_level > 9) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA, "Unsupported compression level passed to the PNG saver"); return FALSE; } } else { g_warning ("Bad option name '%s' passed to the PNG saver", *kiter); return FALSE; } ++kiter; ++viter; } } width = cairo_image_surface_get_width (image); height = cairo_image_surface_get_height (image); alpha = _cairo_image_surface_get_has_alpha (image); pixels = _cairo_image_surface_flush_and_get_data (image); rowstride = cairo_image_surface_get_stride (image); cairo_png_data = g_new0 (CairoPngData, 1); cairo_png_data->error = error; cairo_png_data->buffer_data = gth_buffer_data_new (); cairo_png_data->png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, &cairo_png_data->error, gerror_error_func, gerror_warning_func); if (cairo_png_data->png_ptr == NULL) { _cairo_png_data_destroy (cairo_png_data); return FALSE; } cairo_png_data->png_info_ptr = png_create_info_struct (cairo_png_data->png_ptr); if (cairo_png_data->png_info_ptr == NULL) { _cairo_png_data_destroy (cairo_png_data); return FALSE; } if (PNG_SETJMP (cairo_png_data->png_ptr)) { _cairo_png_data_destroy (cairo_png_data); return FALSE; } png_set_write_fn (cairo_png_data->png_ptr, cairo_png_data, cairo_png_write_data_func, cairo_png_flush_data_func); /* Set the image information here */ png_set_IHDR (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr, width, height, 8, (alpha ? PNG_COLOR_TYPE_RGBA : PNG_COLOR_TYPE_RGB), PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); /* Options */ sig_bit.red = 8; sig_bit.green = 8; sig_bit.blue = 8; if (alpha) sig_bit.alpha = 8; png_set_sBIT (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr, &sig_bit); png_set_compression_level (cairo_png_data->png_ptr, compression_level); /* Write the file header information. */ png_write_info (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr); /* Write the image */ bpp = alpha ? 4 : 3; buf = g_new (guchar, width * bpp); ptr = pixels; for (row = 0; row < height; ++row) { _cairo_copy_line_as_rgba_big_endian (buf, ptr, width, alpha); png_write_rows (cairo_png_data->png_ptr, &buf, 1); ptr += rowstride; } g_free (buf); png_write_end (cairo_png_data->png_ptr, cairo_png_data->png_info_ptr); gth_buffer_data_get (cairo_png_data->buffer_data, buffer, buffer_size); _cairo_png_data_destroy (cairo_png_data); return TRUE; }
bool PNG::Encode (ImageBuffer *imageBuffer, ByteArray *bytes) { png_structp png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, NULL, user_error_fn, user_warning_fn); if (!png_ptr) { return false; } png_infop info_ptr = png_create_info_struct (png_ptr); if (!info_ptr) { return false; } if (setjmp (png_jmpbuf (png_ptr))) { png_destroy_write_struct (&png_ptr, &info_ptr); return false; } QuickVec<uint8> out_buffer; png_set_write_fn (png_ptr, &out_buffer, user_write_data, user_flush_data); int w = imageBuffer->width; int h = imageBuffer->height; int bit_depth = 8; //int color_type = (inSurface->Format () & pfHasAlpha) ? PNG_COLOR_TYPE_RGB_ALPHA : PNG_COLOR_TYPE_RGB; int color_type = PNG_COLOR_TYPE_RGB_ALPHA; png_set_IHDR (png_ptr, info_ptr, w, h, bit_depth, color_type, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); png_write_info (png_ptr, info_ptr); bool do_alpha = (color_type == PNG_COLOR_TYPE_RGBA); unsigned char* imageData = imageBuffer->data->Bytes(); int stride = w * imageBuffer->bpp; { QuickVec<uint8> row_data (w * 4); png_bytep row = &row_data[0]; for (int y = 0; y < h; y++) { uint8 *buf = &row_data[0]; const uint8 *src = (const uint8 *)(imageData + (stride * y)); for (int x = 0; x < w; x++) { buf[0] = src[0]; buf[1] = src[1]; buf[2] = src[2]; src += 3; buf += 3; if (do_alpha) { *buf++ = *src; } src++; } png_write_rows (png_ptr, &row, 1); } } png_write_end (png_ptr, NULL); *bytes = ByteArray (out_buffer); return true; }