def __init__(self, surface, filename, rect, alpha, single_tile_pattern=False, save_srgb_chunks=False, **kwargs): super(PNGFileUpdateTask, self).__init__() self._final_filename = filename # Sizes. Save at least one tile to allow empty docs to be written if not rect: rect = surface.get_bbox() x, y, w, h = rect if w == 0 or h == 0: x, y, w, h = (0, 0, 1, 1) rect = (x, y, w, h) # Snapshot and recreate clone_surface = Surface( looped=surface.looped, looped_size=surface.looped_size, ) clone_surface.load_snapshot(surface.save_snapshot()) # Open a tempfile for writing tmp_filename = filename + ".tmp" if os.path.exists(tmp_filename): os.unlink(tmp_filename) tmp_fp = open(tmp_filename, "wb") self._png_writer = mypaintlib.ProgressivePNGWriter( tmp_fp, w, h, alpha, save_srgb_chunks, ) self._tmp_filename = tmp_filename self._tmp_fp = tmp_fp # What to write self._strips_iter = lib.surface.scanline_strips_iter( clone_surface, rect, alpha=alpha, single_tile_pattern=single_tile_pattern, **kwargs) logger.debug("autosave: scheduled update of %r", self._final_filename)
def save_as_png(surface, filename, *rect, **kwargs): """Saves a tile-blittable surface to a file in PNG format :param TileBlittable surface: Surface to save :param unicode filename: The file to write :param tuple \*rect: Rectangle (x, y, w, h) to save :param bool alpha: If true, write a PNG with alpha :param callable feedback_cb: Called every TILES_PER_CALLBACK tiles. :param bool single_tile_pattern: True if surface is a one tile only. :param bool save_srgb_chunks: Set to False to not save sRGB flags. :param tuple \*\*kwargs: Passed to blit_tile_into (minus the above) The `alpha` parameter is passed to the surface's `blit_tile_into()` method, as well as to the PNG writer. Rendering is skipped for all but the first line for single-tile patterns. If `*rect` is left unspecified, the surface's own bounding box will be used. If `save_srgb_chunks` is set to False, sRGB (and associated fallback cHRM and gAMA) will not be saved. MyPaint's default behaviour is currently to save these chunks. Raises `lib.errors.FileHandlingError` with a descriptive string if something went wrong. """ # Horrible, dirty argument handling alpha = kwargs.pop('alpha', False) feedback_cb = kwargs.pop('feedback_cb', None) single_tile_pattern = kwargs.pop("single_tile_pattern", False) save_srgb_chunks = kwargs.pop("save_srgb_chunks", True) # Sizes. Save at least one tile to allow empty docs to be written if not rect: rect = surface.get_bbox() x, y, w, h = rect if w == 0 or h == 0: x, y, w, h = (0, 0, 1, 1) rect = (x, y, w, h) try: logger.debug( "Writing %r (%dx%d) alpha=%r srgb=%r", filename, w, h, alpha, save_srgb_chunks, ) with open(filename, "wb") as writer_fp: pngsave = mypaintlib.ProgressivePNGWriter( writer_fp, w, h, alpha, save_srgb_chunks, ) feedback_counter = 0 scanline_strips = scanline_strips_iter( surface, rect, alpha=alpha, single_tile_pattern=single_tile_pattern, **kwargs) for scanline_strip in scanline_strips: pngsave.write(scanline_strip) if feedback_cb and feedback_counter % TILES_PER_CALLBACK == 0: feedback_cb() feedback_counter += 1 pngsave.close() logger.debug("Finished writing %r", filename) except (IOError, OSError, RuntimeError) as err: logger.exception( "Caught %r from C++ png-writer code, re-raising as a " "FileHandlingError", err, ) raise FileHandlingError( C_( "low-level PNG writer failure report (dialog)", u"Failed to write “{basename}”.\n\n" u"Reason: {err}\n" u"Target folder: “{dirname}”.").format( err=err, basename=os.path.basename(filename), dirname=os.path.dirname(filename), ))
def save_as_png(surface, filename, *rect, **kwargs): """Saves a tile-blittable surface to a file in PNG format :param TileBlittable surface: Surface to save :param unicode filename: The file to write :param tuple \*rect: Rectangle (x, y, w, h) to save :param bool alpha: If true, write a PNG with alpha :param callable feedback_cb: Called every TILES_PER_CALLBACK tiles. :param bool single_tile_pattern: True if surface is a one tile only. :param bool save_srgb_chunks: Set to False to not save sRGB flags. :param tuple \*\*kwargs: Passed to blit_tile_into (minus the above) The `alpha` parameter is passed to the surface's `blit_tile_into()` method, as well as to the PNG writer. Rendering is skipped for all but the first line for single-tile patterns. If `*rect` is left unspecified, the surface's own bounding box will be used. If `save_srgb_chunks` is set to False, sRGB (and associated fallback cHRM and gAMA) will not be saved. MyPaint's default behaviour is currently to save these chunks. Raises `lib.errors.FileHandlingError` with a descriptive string if something went wrong. """ # Horrible, dirty argument handling alpha = kwargs.pop('alpha', False) feedback_cb = kwargs.pop('feedback_cb', None) single_tile_pattern = kwargs.pop("single_tile_pattern", False) save_srgb_chunks = kwargs.pop("save_srgb_chunks", True) # Sizes. Save at least one tile to allow empty docs to be written if not rect: rect = surface.get_bbox() x, y, w, h = rect if w == 0 or h == 0: x, y, w, h = (0, 0, 1, 1) rect = (x, y, w, h) writer_fp = None try: writer_fp = open(filename, "wb") logger.debug( "Writing %r (%dx%d) alpha=%r srgb=%r", filename, w, h, alpha, save_srgb_chunks, ) pngsave = mypaintlib.ProgressivePNGWriter( writer_fp, w, h, alpha, save_srgb_chunks, ) feedback_counter = 0 scanline_strips = scanline_strips_iter( surface, rect, alpha=alpha, single_tile_pattern=single_tile_pattern, **kwargs) for scanline_strip in scanline_strips: pngsave.write(scanline_strip) if feedback_cb and feedback_counter % TILES_PER_CALLBACK == 0: feedback_cb() feedback_counter += 1 pngsave.close() logger.debug("Finished writing %r", filename) except (IOError, OSError, RuntimeError) as err: raise FileHandlingError(_("PNG write failed: %s") % (err, )) # Other possible exceptions include TypeError, ValueError, but # those indicate incorrect coding usually; just raise them # normally. finally: if writer_fp: writer_fp.close()