private List<byte[]> doLayering(PngImage baseImage, PngImage layerImage, List<byte[]> baseRows, List<byte[]> layerRows) throws IOException {
		final List<byte[]> result = new ArrayList<>(baseRows.size());

		final PngImageType baseImageType = PngImageType.forColorType(baseImage.getColorType());
		final PngImageType layerImageType = PngImageType.forColorType(layerImage.getColorType());
		final int sampleSize = baseImage.getSampleBitCount();

		for (int rowIndex = 0; rowIndex < baseRows.size(); rowIndex++) {
			final byte[] baseRow = baseRows.get(rowIndex);
			final byte[] layerRow = layerRows.get(rowIndex);
			final int sampleCount = ((baseRow.length - 1) * 8) / sampleSize;

			final ByteArrayInputStream baseIn = new ByteArrayInputStream(baseRow);
			final DataInputStream baseDin = new DataInputStream(baseIn);
			final int filterByte = baseDin.readUnsignedByte();

			final ByteArrayInputStream layerIn = new ByteArrayInputStream(layerRow);
			final DataInputStream layerDin = new DataInputStream(layerIn);
			layerDin.readUnsignedByte();	// skip filter byte

			final ByteArrayOutputStream outs = new ByteArrayOutputStream(baseRow.length);
			final DataOutputStream dos = new DataOutputStream(outs);
			dos.writeByte(filterByte);

			for (int i = 0; i < sampleCount; i++) {
				// Zero alpha represents a completely transparent pixel,
				// maximum alpha represents a completely opaque pixel.
				int baseRed, baseGreen, baseBlue, baseAlpha = 0;
				int layerRed, layerGreen, layerBlue, layerAlpha = 0;

				if (baseImage.getBitDepth() == 8) {
					baseRed = baseDin.readUnsignedByte();
					baseGreen = baseDin.readUnsignedByte();
					baseBlue = baseDin.readUnsignedByte();
				} else {
					baseRed = baseDin.readUnsignedShort();
					baseGreen = baseDin.readUnsignedShort();
					baseBlue = baseDin.readUnsignedShort();
				}

				if (baseImageType == PngImageType.TRUECOLOR_ALPHA) {
					baseAlpha = (baseImage.getBitDepth() == 8)
						? baseDin.readUnsignedByte()
						: baseDin.readUnsignedShort();
				} else {
					baseAlpha = 255;
				}

				if (layerImage.getBitDepth() == 8) {
					layerRed = layerDin.readUnsignedByte();
					layerGreen = layerDin.readUnsignedByte();
					layerBlue = layerDin.readUnsignedByte();
				} else {
					layerRed = layerDin.readUnsignedShort();
					layerGreen = layerDin.readUnsignedShort();
					layerBlue = layerDin.readUnsignedShort();
				}

				if (layerImageType == PngImageType.TRUECOLOR_ALPHA) {
					layerAlpha = (layerImage.getBitDepth() == 8)
						? layerDin.readUnsignedByte()
						: layerDin.readUnsignedShort();
				}

				if (layerAlpha == 0) {
					dos.writeByte(baseRed);
					dos.writeByte(baseGreen);
					dos.writeByte(baseBlue);
					dos.writeByte(baseAlpha);
				} else {
					dos.writeByte((baseRed * (255 - layerAlpha) + layerRed * layerAlpha) / 255);
					dos.writeByte((baseGreen * (255 - layerAlpha) + layerGreen * layerAlpha) / 255);
					dos.writeByte((baseBlue * (255 - layerAlpha) + layerBlue * layerAlpha) / 255);
					dos.writeByte(255);
				}
			}
			dos.flush();
			result.add(outs.toByteArray());
		}
		return result;
	}