diff --git a/minetest/png.lua b/minetest/png.lua
index 93641ad..49c3474 100644
--- a/minetest/png.lua
+++ b/minetest/png.lua
@@ -72,6 +72,13 @@ local samples = {
 	truecolor = 3
 }
 
+local adam7_passes = {
+	x_min = { 0, 4, 0, 2, 0, 1, 0 },
+	y_min = { 0, 0, 4, 0, 2, 0, 1 },
+	x_step = { 8, 8, 4, 4, 2, 2, 1 },
+	y_step = { 8, 8, 8, 4, 4, 2, 2 },
+}
+
 (...).decode_png = function(stream)
 	local chunk_crc
 	local function read(n)
@@ -125,7 +132,6 @@ local samples = {
 	local interlace_method = byte()
 	assert(interlace_method <= 1, "unsupported interlace method")
 	local adam7 = interlace_method == 1
-	assert(not adam7, "adam7 interlacing not supported yet")
 	check_crc() -- IHDR CRC
 
 	local palette
@@ -205,13 +211,23 @@ local samples = {
 			(64 bits required, packing non-mantissa bits isn't practical) => separate table with alpha values
 	]]
 	local data = {}
-	local alpha_data = {}
+	local alpha_data = color_type.color == "truecolor" and bit_depth == 16 and {} or nil
+	if adam7 then
+		-- Allocate space in list part in order to not fill the hash part later
+		for i = 1, width * height do
+			data[i] = false
+			if alpha_data then
+				alpha_data[i] = false
+			end
+		end
+	end
 	local bits_per_pixel = (samples[color_type.color] + (color_type.alpha and 1 or 0)) * bit_depth
 	local bytes_per_pixel = math.ceil(bits_per_pixel / 8)
-	local scanline_bytecount = math.ceil(width * bits_per_pixel / 8)
 	local previous_scanline
-	for y = 0, height - 1 do
-		local idat_base_index = y * (scanline_bytecount + 1) + 1
+	local idat_base_index = 1
+	local function read_scanline(x_min, x_step, y)
+		local scanline_width = math.ceil((width - x_min) / x_step)
+		local scanline_bytecount = math.ceil(scanline_width * bits_per_pixel / 8)
 		local filtering = idat_content:byte(idat_base_index)
 		local scanline = {}
 		for i = 1, scanline_bytecount do
@@ -262,7 +278,7 @@ local samples = {
 			local low = 2^(-bit % 8)
 			return floor(byte / low) % (2^bit_depth)
 		end
-		for x = 0, width - 1 do
+		for x = x_min, width - 1, x_step do
 			local data_index = y * width + x + 1
 			if color_type.color == "palette" then
 				local palette_index = sample()
@@ -303,6 +319,23 @@ local samples = {
 		-- Each byte of the scanline must have been read from
 		assert(bit >= #scanline * 8 - 7)
 		previous_scanline = scanline
+		idat_base_index = idat_base_index + scanline_bytecount + 1
+	end
+	if adam7 then
+		for pass = 1, 7 do
+			local x_min, y_min = adam7_passes.x_min[pass], adam7_passes.y_min[pass]
+			if x_min < width and y_min < height then -- Non-empty pass
+				local x_step, y_step = adam7_passes.x_step[pass], adam7_passes.y_step[pass]
+				previous_scanline = nil -- Filtering doesn't use scanlines of previous passes
+				for y = y_min, height - 1, y_step do
+					read_scanline(x_min, x_step, y)
+				end
+			end
+		end
+	else
+		for y = 0, height - 1 do
+			read_scanline(0, 1, y)
+		end
 	end
 	return {
 		width = width,
@@ -310,7 +343,7 @@ local samples = {
 		color_type = color_type,
 		source_gamma = source_gamma,
 		data = data,
-		alpha_data = next(alpha_data) and alpha_data
+		alpha_data = alpha_data
 	}
 end