static VALUE write_png2(VALUE *args) { struct io_write *data; png_structp png_ptr = (png_structp)args[0]; png_infop info_ptr = (png_infop)args[1]; VALUE scanline, image_in = args[2]; size_t i; write_configure(image_in, png_ptr, info_ptr); png_write_info(png_ptr, info_ptr); for (i = 0; i < png_get_image_height(png_ptr, info_ptr); i++) { scanline = rb_funcall(image_in, id_gets, 0); write_scanline(scanline, png_ptr, info_ptr); } png_write_end(png_ptr, info_ptr); data = (struct io_write *)png_get_io_ptr(png_ptr); return INT2FIX(data->total); }
int write_scanline_rgb16 (TIFF *tif, uint16 *buffer, tsize_t size) { return write_scanline (tif, (tdata_t) buffer); }
int write_scanline_gray8 (TIFF *tif, uint8 *buffer, tsize_t size) { return write_scanline (tif, (tdata_t) buffer); }
bool ImageOutputWrap::write_scanline_bt (int y, int z, TypeDesc::BASETYPE format, object &buffer, stride_t xstride) { return write_scanline (y, z, format, buffer, xstride); }
bool ImageOutput::write_image (TypeDesc format, const void *data, stride_t xstride, stride_t ystride, stride_t zstride, ProgressCallback progress_callback, void *progress_callback_data) { bool native = (format == TypeDesc::UNKNOWN); stride_t pixel_bytes = (stride_t) m_spec.pixel_bytes (native); if (native && xstride == AutoStride) xstride = pixel_bytes; m_spec.auto_stride (xstride, ystride, zstride, format, m_spec.nchannels, m_spec.width, m_spec.height); if (supports ("rectangles")) { // Use a rectangle if we can return write_rectangle (0, m_spec.width-1, 0, m_spec.height-1, 0, m_spec.depth-1, format, data, xstride, ystride, zstride); } bool ok = true; if (progress_callback) if (progress_callback (progress_callback_data, 0.0f)) return ok; if (m_spec.tile_width && supports ("tiles")) { // Tiled image // FIXME: what happens if the image dimensions are smaller than // the tile dimensions? Or if one of the tiles runs past the // right or bottom edge? Do we need to allocate a full tile and // copy into it before calling write_tile? That's probably the // safe thing to do. Or should that handling be pushed all the // way into write_tile itself? // Locally allocate a single tile to gracefully deal with image // dimensions smaller than a tile, or if one of the tiles runs // past the right or bottom edge. Then we copy from our tile to // the user data, only copying valid pixel ranges. size_t tilexstride = pixel_bytes; size_t tileystride = tilexstride * m_spec.tile_width; size_t tilezstride = tileystride * m_spec.tile_height; size_t tile_pixels = (size_t)m_spec.tile_width * (size_t)m_spec.tile_height * (size_t)std::max(1,m_spec.tile_depth); std::vector<char> pels (tile_pixels * pixel_bytes); for (int z = 0; z < m_spec.depth; z += m_spec.tile_depth) for (int y = 0; y < m_spec.height; y += m_spec.tile_height) { for (int x = 0; x < m_spec.width && ok; x += m_spec.tile_width) { // Now copy out the scanlines // FIXME -- can we do less work for the tiles that // don't overlap image boundaries? int ntz = std::min (z+m_spec.tile_depth, m_spec.depth) - z; int nty = std::min (y+m_spec.tile_height, m_spec.height) - y; int ntx = std::min (x+m_spec.tile_width, m_spec.width) - x; for (int tz = 0; tz < ntz; ++tz) { for (int ty = 0; ty < nty; ++ty) { // FIXME -- doesn't work for non-contiguous scanlines memcpy (&pels[ty*tileystride+tz*tilezstride], (char *)data + x*xstride + (y+ty)*ystride + (z+tz)*zstride, ntx*tilexstride); } } ok &= write_tile (x+m_spec.x, y+m_spec.y, z+m_spec.z, format, &pels[0]); } if (progress_callback) if (progress_callback (progress_callback_data, (float)y/m_spec.height)) return ok; } } else { // Scanline image for (int z = 0; z < m_spec.depth; ++z) for (int y = 0; y < m_spec.height && ok; ++y) { ok &= write_scanline (y+m_spec.y, z+m_spec.z, format, (const char *)data + z*zstride + y*ystride, xstride); if (progress_callback && !(y & 0x0f)) if (progress_callback (progress_callback_data, (float)y/m_spec.height)) return ok; } } if (progress_callback) progress_callback (progress_callback_data, 1.0f); return ok; }