diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c
index 50e4adfacf31196d52ebbbdfc7d2ba1451c8d2b9..8ac5b0ec832e3634a6d810e80e31fbf160118657 100644
--- a/src/hardware/hw_cache.c
+++ b/src/hardware/hw_cache.c
@@ -148,7 +148,7 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm
 	}
 }
 
-static void HWR_DrawPostsInCache(const doompost_t *patchcol, UINT8 *block, GLMipmap_t *mipmap,
+static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap,
 								INT32 pblockheight, INT32 blockmodulo,
 								fixed_t yfracstep, fixed_t scale_y,
 								texpatch_t *originPatch, INT32 patchheight,
@@ -157,116 +157,7 @@ static void HWR_DrawPostsInCache(const doompost_t *patchcol, UINT8 *block, GLMip
 	fixed_t yfrac, position, count;
 	UINT8 *dest;
 	const UINT8 *source;
-	INT32 topdelta, prevdelta = -1;
-	INT32 originy = 0;
-
-	// for writing a pixel to dest
-	RGBA_t colortemp;
-	UINT8 alpha;
-	UINT8 texel;
-	UINT16 texelu16;
-
-	(void)patchheight; // This parameter is unused
-
-	if (originPatch) // originPatch can be NULL here, unlike in the software version
-		originy = originPatch->originy;
-
-	while (patchcol->topdelta != 0xff)
-	{
-		topdelta = patchcol->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		source = (const UINT8 *)patchcol + 3;
-		count  = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
-		position = originy + topdelta;
-
-		yfrac = 0;
-		//yfracstep = (patchcol->length << FRACBITS) / count;
-		if (position < 0)
-		{
-			yfrac = -position<<FRACBITS;
-			count += (((position * scale_y) + (FRACUNIT/2)) >> FRACBITS);
-			position = 0;
-		}
-
-		position = ((position * scale_y) + (FRACUNIT/2)) >> FRACBITS;
-
-		if (position < 0)
-			position = 0;
-
-		if (position + count >= pblockheight)
-			count = pblockheight - position;
-
-		dest = block + (position*blockmodulo);
-		while (count > 0)
-		{
-			count--;
-
-			texel = source[yfrac>>FRACBITS];
-			alpha = 0xFF;
-			// Make pixel transparent if chroma keyed
-			if ((mipmap->flags & TF_CHROMAKEYED) && (texel == HWR_PATCHES_CHROMAKEY_COLORINDEX))
-				alpha = 0x00;
-
-			if (mipmap->colormap)
-				texel = mipmap->colormap->data[texel];
-
-			switch (bpp)
-			{
-				case 2:
-					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-						texel = ASTBlendPaletteIndexes(*(dest+1), texel, originPatch->style, originPatch->alpha);
-					texelu16 = (UINT16)((alpha<<8) | texel);
-					memcpy(dest, &texelu16, sizeof(UINT16));
-					break;
-				case 3:
-					colortemp = V_GetColor(texel);
-					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-					{
-						RGBA_t rgbatexel;
-						rgbatexel.rgba = *(UINT32 *)dest;
-						colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
-					}
-					memcpy(dest, &colortemp, sizeof(RGBA_t)-sizeof(UINT8));
-					break;
-				case 4:
-					colortemp = V_GetColor(texel);
-					colortemp.s.alpha = alpha;
-					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-					{
-						RGBA_t rgbatexel;
-						rgbatexel.rgba = *(UINT32 *)dest;
-						colortemp.rgba = ASTBlendTexturePixel(rgbatexel, colortemp, originPatch->style, originPatch->alpha);
-					}
-					memcpy(dest, &colortemp, sizeof(RGBA_t));
-					break;
-				// default is 1
-				default:
-					if ((originPatch != NULL) && (originPatch->style != AST_COPY))
-						*dest = ASTBlendPaletteIndexes(*dest, texel, originPatch->style, originPatch->alpha);
-					else
-						*dest = texel;
-					break;
-			}
-
-			dest += blockmodulo;
-			yfrac += yfracstep;
-		}
-		patchcol = (const doompost_t *)((const UINT8 *)patchcol + patchcol->length + 4);
-	}
-}
-
-static void HWR_DrawFlippedPostsInCache(const doompost_t *patchcol, UINT8 *block, GLMipmap_t *mipmap,
-								INT32 pblockheight, INT32 blockmodulo,
-								fixed_t yfracstep, fixed_t scale_y,
-								texpatch_t *originPatch, INT32 patchheight,
-								INT32 bpp)
-{
-	fixed_t yfrac, position, count;
-	UINT8 *dest;
-	const UINT8 *source;
-	INT32 topdelta, prevdelta = -1;
+	INT32 topdelta;
 	INT32 originy = 0;
 
 	// for writing a pixel to dest
@@ -278,18 +169,15 @@ static void HWR_DrawFlippedPostsInCache(const doompost_t *patchcol, UINT8 *block
 	if (originPatch) // originPatch can be NULL here, unlike in the software version
 		originy = originPatch->originy;
 
-	while (patchcol->topdelta != 0xff)
+	for (size_t i = 0; i < patchcol->num_posts; i++)
 	{
-		topdelta = patchcol->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topdelta = patchheight-patchcol->length-topdelta;
-		source = (const UINT8 *)patchcol + 3;
-		count  = ((patchcol->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
+		post_t *post = &patchcol->posts[i];
+		source = patchcol->pixels + post->data_offset;
+		topdelta = patchheight-post->length-post->topdelta;
+		count  = ((post->length * scale_y) + (FRACUNIT/2)) >> FRACBITS;
 		position = originy + topdelta;
 
-		yfrac = (patchcol->length-1) << FRACBITS;
+		yfrac = (post->length-1) << FRACBITS;
 
 		if (position < 0)
 		{
@@ -361,7 +249,6 @@ static void HWR_DrawFlippedPostsInCache(const doompost_t *patchcol, UINT8 *block
 			dest += blockmodulo;
 			yfrac -= yfracstep;
 		}
-		patchcol = (const doompost_t *)((const UINT8 *)patchcol + patchcol->length + 4);
 	}
 }
 
@@ -419,19 +306,19 @@ static void HWR_DrawPatchInCache(GLMipmap_t *mipmap,
 static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	INT32 pblockwidth, INT32 pblockheight,
 	texture_t *texture, texpatch_t *patch,
-	const softwarepatch_t *realpatch)
+	const patch_t *realpatch)
 {
 	INT32 x, x1, x2;
 	INT32 col, ncols;
 	fixed_t xfrac, xfracstep;
 	fixed_t yfracstep, scale_y;
-	const doompost_t *patchcol;
+	const column_t *patchcol;
 	UINT8 *block = mipmap->data;
 	INT32 bpp;
 	INT32 blockmodulo;
 	INT32 width, height;
 	// Column drawing function pointer.
-	static void (*ColumnDrawerPointer)(const doompost_t *patchcol, UINT8 *block, GLMipmap_t *mipmap,
+	static void (*ColumnDrawerPointer)(const column_t *patchcol, UINT8 *block, GLMipmap_t *mipmap,
 								INT32 pblockheight, INT32 blockmodulo,
 								fixed_t yfracstep, fixed_t scale_y,
 								texpatch_t *originPatch, INT32 patchheight,
@@ -440,11 +327,11 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	if (texture->width <= 0 || texture->height <= 0)
 		return;
 
-	ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedPostsInCache : HWR_DrawPostsInCache;
+	ColumnDrawerPointer = (patch->flip & 2) ? HWR_DrawFlippedColumnInCache : HWR_DrawColumnInCache;
 
 	x1 = patch->originx;
-	width = SHORT(realpatch->width);
-	height = SHORT(realpatch->height);
+	width = realpatch->width;
+	height = realpatch->height;
 	x2 = x1 + width;
 
 	if (x1 > texture->width || x2 < 0)
@@ -491,9 +378,9 @@ static void HWR_DrawTexturePatchInCache(GLMipmap_t *mipmap,
 	for (block += col*bpp; ncols--; block += bpp, xfrac += xfracstep)
 	{
 		if (patch->flip & 1)
-			patchcol = (const doompost_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[(width-1)-(xfrac>>FRACBITS)]));
+			patchcol = &realpatch->columns[(width-1)-(xfrac>>FRACBITS)];
 		else
-			patchcol = (const doompost_t *)((const UINT8 *)realpatch + LONG(realpatch->columnofs[xfrac>>FRACBITS]));
+			patchcol = &realpatch->columns[xfrac>>FRACBITS];
 
 		ColumnDrawerPointer(patchcol, block, mipmap,
 								pblockheight, blockmodulo,
@@ -537,8 +424,6 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex)
 	UINT8 *block;
 	texture_t *texture;
 	texpatch_t *patch;
-	softwarepatch_t *realpatch;
-	UINT8 *pdata;
 	INT32 blockwidth, blockheight, blocksize;
 
 	INT32 i;
@@ -590,30 +475,25 @@ static void HWR_GenerateTexture(INT32 texnum, GLMapTexture_t *grtex)
 	// Composite the columns together.
 	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
 	{
-		boolean dealloc = true;
-		size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump);
-		pdata = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
-		realpatch = (softwarepatch_t *)pdata;
+		UINT8 *pdata = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
+		patch_t *realpatch = NULL;
 
 #ifndef NO_PNG_LUMPS
-		if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength))
-			realpatch = (softwarepatch_t *)Picture_PNGConvert(pdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
+		size_t lumplength = W_LumpLengthPwad(patch->wad, patch->lump);
+		if (Picture_IsLumpPNG(pdata, lumplength))
+			realpatch = (patch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
 		else
 #endif
 #ifdef WALLFLATS
 		if (texture->type == TEXTURETYPE_FLAT)
-			realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_DOOMPATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
+			realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
 		else
 #endif
-		{
-			(void)lumplength;
-			dealloc = false;
-		}
+			realpatch = (patch_t *)Picture_Convert(PICFMT_DOOMPATCH, pdata, PICFMT_PATCH, 0, NULL, 0, 0, 0, 0, 0);
 
 		HWR_DrawTexturePatchInCache(&grtex->mipmap, blockwidth, blockheight, texture, patch, realpatch);
 
-		if (dealloc)
-			Z_Unlock(realpatch);
+		Patch_Free(realpatch);
 	}
 	//Hurdler: not efficient at all but I don't remember exactly how HWR_DrawPatchInCache works :(
 	if (format2bpp(grtex->mipmap.format)==4)
diff --git a/src/r_picformats.c b/src/r_picformats.c
index c7be4ea22fce943d6479a0f16b0fedaaf871ef46..b11eae138c1c477a93216383d122ce226043d868 100644
--- a/src/r_picformats.c
+++ b/src/r_picformats.c
@@ -51,8 +51,6 @@
 #endif
 #endif
 
-static unsigned char imgbuf[1<<26];
-
 #ifdef PICTURE_PNG_USELOOKUP
 static colorlookup_t png_colorlookup;
 #endif
@@ -116,13 +114,9 @@ void *Picture_PatchConvert(
 	INT16 inwidth, INT16 inheight, INT16 inleftoffset, INT16 intopoffset,
 	pictureflags_t flags)
 {
-	INT16 x, y;
-	UINT8 *img;
-	UINT8 *imgptr = imgbuf;
-	UINT8 *colpointers, *startofspan;
-	size_t size = 0;
-	patch_t *inpatch = NULL;
+	INT32 outbpp = Picture_FormatBPP(outformat);
 	INT32 inbpp = Picture_FormatBPP(informat);
+	patch_t *inpatch = NULL;
 
 	(void)insize; // ignore
 
@@ -130,16 +124,17 @@ void *Picture_PatchConvert(
 		I_Error("Picture_PatchConvert: input format was PICFMT_NONE!");
 	else if (outformat == PICFMT_NONE)
 		I_Error("Picture_PatchConvert: output format was PICFMT_NONE!");
+	else if (Picture_IsDoomPatchFormat(outformat))
+		I_Error("Picture_PatchConvert: cannot convert to Doom patch!");
 	else if (informat == outformat)
 		I_Error("Picture_PatchConvert: input and output formats were the same!");
 
 	if (inbpp == PICDEPTH_NONE)
-		I_Error("Picture_PatchConvert: unknown input bits per pixel?!");
-	if (Picture_FormatBPP(outformat) == PICDEPTH_NONE)
-		I_Error("Picture_PatchConvert: unknown output bits per pixel?!");
+		I_Error("Picture_PatchConvert: unknown input bits per pixel!");
+	if (outbpp == PICDEPTH_NONE)
+		I_Error("Picture_PatchConvert: unknown output bits per pixel!");
 
-	// If it's a patch, you can just figure out
-	// the dimensions from the header.
+	// If it's a patch, we can just figure out the dimensions from the header.
 	if (Picture_IsPatchFormat(informat))
 	{
 		inpatch = (patch_t *)picture;
@@ -160,28 +155,40 @@ void *Picture_PatchConvert(
 		}
 	}
 
-	// Write image size and offset
-	WRITEINT16(imgptr, inwidth);
-	WRITEINT16(imgptr, inheight);
-	WRITEINT16(imgptr, inleftoffset);
-	WRITEINT16(imgptr, intopoffset);
+	patch_t *out = Z_Calloc(sizeof(patch_t), PU_PATCH, NULL);
+
+	out->width = inwidth;
+	out->height = inheight;
+	out->leftoffset = inleftoffset;
+	out->topoffset = intopoffset;
+
+	size_t max_pixels = out->width * out->height;
+	size_t num_posts = 0;
+
+	out->columns = Z_Calloc(sizeof(column_t) * out->width, PU_PATCH_DATA, NULL);
+	out->pixels = Z_Calloc(max_pixels * (outbpp / 8), PU_PATCH_DATA, NULL);
+	out->posts = NULL;
+
+	UINT8 *imgptr = out->pixels;
 
-	// Leave placeholder to column pointers
-	colpointers = imgptr;
-	imgptr += inwidth*4;
+	size_t *column_posts = Z_Calloc(sizeof(size_t) * inwidth, PU_STATIC, NULL);
 
 	// Write columns
-	for (x = 0; x < inwidth; x++)
+	for (INT16 x = 0; x < inwidth; x++)
 	{
-		int lastStartY = 0;
-		int spanSize = 0;
-		startofspan = NULL;
+		post_t *post;
+		size_t post_data_offset = 0;
+		boolean was_opaque = false;
 
-		// Write column pointer
-		WRITEINT32(colpointers, imgptr - imgbuf);
+		column_t *column = &out->columns[x];
+		column->pixels = imgptr;
+		column->posts = NULL;
+		column->num_posts = 0;
+
+		column_posts[x] = (size_t)-1;
 
 		// Write pixels
-		for (y = 0; y < inheight; y++)
+		for (INT16 y = 0; y < inheight; y++)
 		{
 			void *input = NULL;
 			boolean opaque = false;
@@ -231,58 +238,33 @@ void *Picture_PatchConvert(
 					if (px == TRANSPARENTPIXEL)
 						alpha = 0;
 				}
-				opaque = (alpha > 1);
+
+				opaque = alpha > 1;
 			}
 
 			// End span if we have a transparent pixel
 			if (!opaque)
 			{
-				if (startofspan)
-					WRITEUINT8(imgptr, 0);
-				startofspan = NULL;
+				was_opaque = false;
 				continue;
 			}
 
-			// Start new column if we need to
-			if (!startofspan || spanSize == 255)
+			if (!was_opaque)
 			{
-				int writeY = y;
-
-				// If we reached the span size limit, finish the previous span
-				if (startofspan)
-					WRITEUINT8(imgptr, 0);
-
-				if (y > 254)
-				{
-					// Make sure we're aligned to 254
-					if (lastStartY < 254)
-					{
-						WRITEUINT8(imgptr, 254);
-						WRITEUINT8(imgptr, 0);
-						imgptr += 2;
-						lastStartY = 254;
-					}
-
-					// Write stopgap empty spans if needed
-					writeY = y - lastStartY;
-
-					while (writeY > 254)
-					{
-						WRITEUINT8(imgptr, 254);
-						WRITEUINT8(imgptr, 0);
-						imgptr += 2;
-						writeY -= 254;
-					}
-				}
-
-				startofspan = imgptr;
-				WRITEUINT8(imgptr, writeY);
-				imgptr += 2;
-				spanSize = 0;
-
-				lastStartY = y;
+				num_posts++;
+
+				out->posts = Z_Realloc(out->posts, sizeof(post_t) * num_posts, PU_PATCH_DATA, NULL);
+				post = &out->posts[num_posts - 1];
+				post->topdelta = (size_t)y;
+				post->length = 0;
+				post->data_offset = post_data_offset;
+				if (column_posts[x] == (size_t)-1)
+					column_posts[x] = num_posts - 1;
+				column->num_posts++;
 			}
 
+			was_opaque = true;
+
 			// Write the pixel
 			switch (outformat)
 			{
@@ -291,18 +273,18 @@ void *Picture_PatchConvert(
 				{
 					if (inbpp == PICDEPTH_32BPP)
 					{
-						RGBA_t out = *(RGBA_t *)input;
-						WRITEUINT32(imgptr, out.rgba);
+						RGBA_t px = *(RGBA_t *)input;
+						WRITEUINT32(imgptr, px.rgba);
 					}
 					else if (inbpp == PICDEPTH_16BPP)
 					{
-						RGBA_t out = pMasterPalette[*((UINT16 *)input) & 0xFF];
-						WRITEUINT32(imgptr, out.rgba);
+						RGBA_t px = pMasterPalette[*((UINT16 *)input) & 0xFF];
+						WRITEUINT32(imgptr, px.rgba);
 					}
 					else // PICFMT_PATCH
 					{
-						RGBA_t out = pMasterPalette[*((UINT8 *)input) & 0xFF];
-						WRITEUINT32(imgptr, out.rgba);
+						RGBA_t px = pMasterPalette[*((UINT8 *)input) & 0xFF];
+						WRITEUINT32(imgptr, px.rgba);
 					}
 					break;
 				}
@@ -311,8 +293,8 @@ void *Picture_PatchConvert(
 					if (inbpp == PICDEPTH_32BPP)
 					{
 						RGBA_t in = *(RGBA_t *)input;
-						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
-						WRITEUINT16(imgptr, (0xFF00 | out));
+						UINT8 px = NearestColor(in.s.red, in.s.green, in.s.blue);
+						WRITEUINT16(imgptr, (0xFF00 | px));
 					}
 					else if (inbpp == PICDEPTH_16BPP)
 						WRITEUINT16(imgptr, *(UINT16 *)input);
@@ -324,13 +306,13 @@ void *Picture_PatchConvert(
 					if (inbpp == PICDEPTH_32BPP)
 					{
 						RGBA_t in = *(RGBA_t *)input;
-						UINT8 out = NearestColor(in.s.red, in.s.green, in.s.blue);
-						WRITEUINT8(imgptr, out);
+						UINT8 px = NearestColor(in.s.red, in.s.green, in.s.blue);
+						WRITEUINT8(imgptr, px);
 					}
 					else if (inbpp == PICDEPTH_16BPP)
 					{
-						UINT16 out = *(UINT16 *)input;
-						WRITEUINT8(imgptr, (out & 0xFF));
+						UINT16 px = *(UINT16 *)input;
+						WRITEUINT8(imgptr, (px & 0xFF));
 					}
 					else // PICFMT_PATCH
 						WRITEUINT8(imgptr, *(UINT8 *)input);
@@ -338,40 +320,29 @@ void *Picture_PatchConvert(
 				}
 			}
 
-			spanSize++;
-			startofspan[1] = spanSize;
+			post->length++;
+			post_data_offset += imgptr - column->pixels;
 		}
-
-		if (startofspan)
-			WRITEUINT8(imgptr, 0);
-
-		WRITEUINT8(imgptr, 0xFF);
 	}
 
-	size = imgptr-imgbuf;
-	img = Z_Malloc(size, PU_STATIC, NULL);
-	memcpy(img, imgbuf, size);
+	UINT8 *old_pixels = out->pixels;
+	size_t total_pixels = imgptr - out->pixels;
+	if (total_pixels != max_pixels)
+		out->pixels = Z_Realloc(out->pixels, total_pixels, PU_PATCH_DATA, NULL);
 
-	if (Picture_IsInternalPatchFormat(outformat))
+	for (INT16 x = 0; x < inwidth; x++)
 	{
-		patch_t *converted = Patch_Create((softwarepatch_t *)img, size, NULL);
+		column_t *column = &out->columns[x];
+		column->posts = &out->posts[column_posts[x]];
+		column->pixels = out->pixels + (column->pixels - old_pixels);
+	}
 
-#ifdef HWRENDER
-		Patch_CreateGL(converted);
-#endif
+	Z_Free(column_posts);
 
-		Z_Free(img);
+	if (outsize != NULL)
+		*outsize = sizeof(patch_t);
 
-		if (outsize != NULL)
-			*outsize = sizeof(patch_t);
-		return converted;
-	}
-	else
-	{
-		if (outsize != NULL)
-			*outsize = size;
-		return img;
-	}
+	return (void *)out;
 }
 
 /** Converts a picture to a flat.
diff --git a/src/r_textures.c b/src/r_textures.c
index 5896ab30d09ccfef75a744ffd4680e578c266b46..4021dc916029c474c67bdc8971bbeff3bf47fa53 100644
--- a/src/r_textures.c
+++ b/src/r_textures.c
@@ -78,24 +78,20 @@ static INT32 tidcachelen = 0;
 // R_DrawColumnInCache
 // Clip and draw a column from a patch into a cached post.
 //
-static inline void R_DrawColumnInCache(doompost_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+static inline void R_DrawColumnInCache(column_t *column, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
 {
 	INT32 count, position;
 	UINT8 *source;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = originPatch->originy;
 
 	(void)patchheight; // This parameter is unused
 
-	while (patch->topdelta != 0xff)
+	for (size_t i = 0; i < column->num_posts; i++)
 	{
-		topdelta = patch->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		source = (UINT8 *)patch + 3;
-		count = patch->length;
-		position = originy + topdelta;
+		post_t *post = &column->posts[i];
+		source = column->pixels + post->data_offset;
+		count = post->length;
+		position = originy + post->topdelta;
 
 		if (position < 0)
 		{
@@ -109,8 +105,6 @@ static inline void R_DrawColumnInCache(doompost_t *patch, UINT8 *cache, texpatch
 
 		if (count > 0)
 			M_Memcpy(cache + position, source, count);
-
-		patch = (doompost_t *)((UINT8 *)patch + patch->length + 4);
 	}
 }
 
@@ -118,22 +112,19 @@ static inline void R_DrawColumnInCache(doompost_t *patch, UINT8 *cache, texpatch
 // R_DrawFlippedColumnInCache
 // Similar to R_DrawColumnInCache; it draws the column inverted, however.
 //
-static inline void R_DrawFlippedColumnInCache(doompost_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+static inline void R_DrawFlippedColumnInCache(column_t *column, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
 {
 	INT32 count, position;
 	UINT8 *source, *dest;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = originPatch->originy;
+	INT32 topdelta;
 
-	while (patch->topdelta != 0xff)
+	for (size_t i = 0; i < column->num_posts; i++)
 	{
-		topdelta = patch->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topdelta = patchheight-patch->length-topdelta;
-		source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
-		count = patch->length;
+		post_t *post = &column->posts[i];
+		topdelta = patchheight - post->length - post->topdelta;
+		source = column->pixels + post->data_offset + post->length;
+		count = post->length;
 		position = originy + topdelta;
 
 		if (position < 0)
@@ -152,33 +143,27 @@ static inline void R_DrawFlippedColumnInCache(doompost_t *patch, UINT8 *cache, t
 			for (; dest < cache + position + count; --source)
 				*dest++ = *source;
 		}
-
-		patch = (doompost_t *)((UINT8 *)patch + patch->length + 4);
 	}
 }
 
 //
 // R_DrawBlendColumnInCache
-// Draws a translucent column into the cache, applying a half-cooked equation to get a proper translucency value (Needs code in R_GenerateTexture()).
+// Draws a translucent column into the cache.
 //
-static inline void R_DrawBlendColumnInCache(doompost_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+static inline void R_DrawBlendColumnInCache(column_t *column, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
 {
 	INT32 count, position;
 	UINT8 *source, *dest;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = originPatch->originy;
 
 	(void)patchheight; // This parameter is unused
 
-	while (patch->topdelta != 0xff)
+	for (size_t i = 0; i < column->num_posts; i++)
 	{
-		topdelta = patch->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		source = (UINT8 *)patch + 3;
-		count = patch->length;
-		position = originy + topdelta;
+		post_t *post = &column->posts[i];
+		source = column->pixels + post->data_offset;
+		count = post->length;
+		position = originy + post->topdelta;
 
 		if (position < 0)
 		{
@@ -197,8 +182,6 @@ static inline void R_DrawBlendColumnInCache(doompost_t *patch, UINT8 *cache, tex
 				if (*source != 0xFF)
 					*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
 		}
-
-		patch = (doompost_t *)((UINT8 *)patch + patch->length + 4);
 	}
 }
 
@@ -206,22 +189,19 @@ static inline void R_DrawBlendColumnInCache(doompost_t *patch, UINT8 *cache, tex
 // R_DrawBlendFlippedColumnInCache
 // Similar to the one above except that the column is inverted.
 //
-static inline void R_DrawBlendFlippedColumnInCache(doompost_t *patch, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
+static inline void R_DrawBlendFlippedColumnInCache(column_t *column, UINT8 *cache, texpatch_t *originPatch, INT32 cacheheight, INT32 patchheight)
 {
 	INT32 count, position;
 	UINT8 *source, *dest;
-	INT32 topdelta, prevdelta = -1;
 	INT32 originy = originPatch->originy;
+	INT32 topdelta;
 
-	while (patch->topdelta != 0xff)
+	for (size_t i = 0; i < column->num_posts; i++)
 	{
-		topdelta = patch->topdelta;
-		if (topdelta <= prevdelta)
-			topdelta += prevdelta;
-		prevdelta = topdelta;
-		topdelta = patchheight-patch->length-topdelta;
-		source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
-		count = patch->length;
+		post_t *post = &column->posts[i];
+		topdelta = patchheight - post->length - post->topdelta;
+		source = column->pixels + post->data_offset + post->length;
+		count = post->length;
 		position = originy + topdelta;
 
 		if (position < 0)
@@ -241,8 +221,6 @@ static inline void R_DrawBlendFlippedColumnInCache(doompost_t *patch, UINT8 *cac
 				if (*source != 0xFF)
 					*dest = ASTBlendPaletteIndexes(*dest, *source, originPatch->style, originPatch->alpha);
 		}
-
-		patch = (doompost_t *)((UINT8 *)patch + patch->length + 4);
 	}
 }
 
@@ -380,7 +358,7 @@ UINT8 *R_GenerateTexture(size_t texnum)
 	// Composite the columns together.
 	for (i = 0, patch = texture->patches; i < texture->patchcount; i++, patch++)
 	{
-		static void (*columnDrawer)(doompost_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer.
+		static void (*columnDrawer)(column_t *, UINT8 *, texpatch_t *, INT32, INT32); // Column drawing function pointer.
 		if (patch->style != AST_COPY)
 			columnDrawer = (patch->flip & 2) ? R_DrawBlendFlippedColumnInCache : R_DrawBlendColumnInCache;
 		else
@@ -388,42 +366,36 @@ UINT8 *R_GenerateTexture(size_t texnum)
 
 		UINT16 wadnum = patch->wad;
 		lumpnum_t lumpnum = patch->lump;
-		size_t lumplength = W_LumpLengthPwad(wadnum, lumpnum);
 		UINT8 *pdata = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE);
-		softwarepatch_t *realpatch = (softwarepatch_t *)pdata;
-		boolean dealloc = true;
+		patch_t *realpatch = NULL;
 
 #ifndef NO_PNG_LUMPS
-		if (Picture_IsLumpPNG((UINT8 *)realpatch, lumplength))
-			realpatch = (softwarepatch_t *)Picture_PNGConvert((UINT8 *)realpatch, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
+		size_t lumplength = W_LumpLengthPwad(wadnum, lumpnum);
+		if (Picture_IsLumpPNG(pdata, lumplength))
+			realpatch = (patch_t *)Picture_PNGConvert(pdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, lumplength, NULL, 0);
 		else
 #endif
 #ifdef WALLFLATS
 		if (texture->type == TEXTURETYPE_FLAT)
-			realpatch = (softwarepatch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_DOOMPATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
+			realpatch = (patch_t *)Picture_Convert(PICFMT_FLAT, pdata, PICFMT_PATCH, 0, NULL, texture->width, texture->height, 0, 0, 0);
 		else
 #endif
-		{
-			(void)lumplength;
-			dealloc = false;
-		}
+			realpatch = (patch_t *)Picture_Convert(PICFMT_DOOMPATCH, pdata, PICFMT_PATCH, 0, NULL, 0, 0, 0, 0, 0);
 
 		x1 = patch->originx;
-		width = SHORT(realpatch->width);
-		height = SHORT(realpatch->height);
+		width = realpatch->width;
+		height = realpatch->height;
 		x2 = x1 + width;
 
 		if (x1 > texture->width || x2 < 0)
 		{
-			if (dealloc)
-				Z_Free(realpatch);
+			Patch_Free(realpatch);
 			continue; // patch not located within texture's x bounds, ignore
 		}
 
 		if (patch->originy > texture->height || (patch->originy + height) < 0)
 		{
-			if (dealloc)
-				Z_Free(realpatch);
+			Patch_Free(realpatch);
 			continue; // patch not located within texture's y bounds, ignore
 		}
 
@@ -442,17 +414,16 @@ UINT8 *R_GenerateTexture(size_t texnum)
 
 		for (; x < x2; x++)
 		{
-			doompost_t *patchcol;
+			column_t *patchcol;
 			if (patch->flip & 1)
-				patchcol = (doompost_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x]));
+				patchcol = &realpatch->columns[(x1+width-1)-x];
 			else
-				patchcol = (doompost_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1]));
+				patchcol = &realpatch->columns[x-x1];
 
 			columnDrawer(patchcol, columns[x].pixels, patch, texture->height, height);
 		}
 
-		if (dealloc)
-			Z_Free(realpatch);
+		Patch_Free(realpatch);
 	}
 
 done:
diff --git a/src/w_wad.c b/src/w_wad.c
index 171eab4f31bc246281424157a3f35a047d9d8c4f..099b9381df992fff1da00ae2f22b310dd630f22c 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -2056,12 +2056,16 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag)
 
 #ifndef NO_PNG_LUMPS
 		if (Picture_IsLumpPNG((UINT8 *)lumpdata, len))
-			ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &len, 0);
+		{
+			ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_PATCH, NULL, NULL, NULL, NULL, len, &len, 0);
+			Z_ChangeTag(ptr, tag);
+			Z_SetUser(ptr, &lumpcache[lump]);
+			return lumpcache[lump];
+		}
 #endif
 
 		dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]);
 		Patch_Create(ptr, len, dest);
-
 		Z_Free(ptr);
 	}
 	else