summaryrefslogtreecommitdiff
path: root/src/i_scale.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/i_scale.c')
-rw-r--r--src/i_scale.c404
1 files changed, 404 insertions, 0 deletions
diff --git a/src/i_scale.c b/src/i_scale.c
new file mode 100644
index 00000000..29bc8731
--- /dev/null
+++ b/src/i_scale.c
@@ -0,0 +1,404 @@
+
+#include "doomdef.h"
+#include "doomtype.h"
+
+#include "z_zone.h"
+
+// Should be screens[0]
+
+static byte *src_buffer;
+
+// Destination buffer, ie. screen->pixels.
+
+static byte *dest_buffer;
+
+// Pitch of destination buffer, ie. screen->pitch.
+
+static int dest_pitch;
+
+// Lookup tables used for aspect ratio correction stretching code.
+// stretch_tables[0] : 20% / 80%
+// stretch_tables[1] : 40% / 60%
+// All other combinations can be reached from these two tables.
+
+static byte *stretch_tables[2];
+
+void I_InitScale(byte *_src_buffer, byte *_dest_buffer, int _dest_pitch)
+{
+ src_buffer = _src_buffer;
+ dest_buffer = _dest_buffer;
+ dest_pitch = _dest_pitch;
+}
+
+void I_Scale1x(int x1, int y1, int x2, int y2)
+{
+ byte *bufp, *screenp;
+ int y;
+ int w = x2 - x1;
+
+ // Need to byte-copy from buffer into the screen buffer
+
+ bufp = src_buffer + y1 * SCREENWIDTH + x1;
+ screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;
+
+ for (y=y1; y<y2; ++y)
+ {
+ memcpy(screenp, bufp, w);
+ screenp += dest_pitch;
+ bufp += SCREENWIDTH;
+ }
+}
+
+void I_Scale2x(int x1, int y1, int x2, int y2)
+{
+ byte *bufp, *screenp, *screenp2;
+ int x, y;
+ int multi_pitch;
+
+ multi_pitch = dest_pitch * 2;
+ bufp = src_buffer + y1 * SCREENWIDTH + x1;
+ screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 2;
+ screenp2 = screenp + dest_pitch;
+
+ for (y=y1; y<y2; ++y)
+ {
+ byte *sp, *sp2, *bp;
+ sp = screenp;
+ sp2 = screenp2;
+ bp = bufp;
+
+ for (x=x1; x<x2; ++x)
+ {
+ *sp++ = *bp; *sp++ = *bp;
+ *sp2++ = *bp; *sp2++ = *bp;
+ ++bp;
+ }
+ screenp += multi_pitch;
+ screenp2 += multi_pitch;
+ bufp += SCREENWIDTH;
+ }
+}
+
+void I_Scale3x(int x1, int y1, int x2, int y2)
+{
+ byte *bufp, *screenp, *screenp2, *screenp3;
+ int x, y;
+ int multi_pitch;
+
+ multi_pitch = dest_pitch * 3;
+ bufp = src_buffer + y1 * SCREENWIDTH + x1;
+ screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 3;
+ screenp2 = screenp + dest_pitch;
+ screenp3 = screenp + dest_pitch * 2;
+
+ for (y=y1; y<y2; ++y)
+ {
+ byte *sp, *sp2, *sp3, *bp;
+ sp = screenp;
+ sp2 = screenp2;
+ sp3 = screenp3;
+ bp = bufp;
+
+ for (x=x1; x<x2; ++x)
+ {
+ *sp++ = *bp; *sp++ = *bp; *sp++ = *bp;
+ *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;
+ *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;
+ ++bp;
+ }
+ screenp += multi_pitch;
+ screenp2 += multi_pitch;
+ screenp3 += multi_pitch;
+ bufp += SCREENWIDTH;
+ }
+}
+
+void I_Scale4x(int x1, int y1, int x2, int y2)
+{
+ byte *bufp, *screenp, *screenp2, *screenp3, *screenp4;
+ int x, y;
+ int multi_pitch;
+
+ multi_pitch = dest_pitch * 4;
+ bufp = src_buffer + y1 * SCREENWIDTH + x1;
+ screenp = (byte *) dest_buffer + (y1 * dest_pitch + x1) * 4;
+ screenp2 = screenp + dest_pitch;
+ screenp3 = screenp + dest_pitch * 2;
+ screenp4 = screenp + dest_pitch * 3;
+
+ for (y=y1; y<y2; ++y)
+ {
+ byte *sp, *sp2, *sp3, *sp4, *bp;
+ sp = screenp;
+ sp2 = screenp2;
+ sp3 = screenp3;
+ sp4 = screenp4;
+ bp = bufp;
+
+ for (x=x1; x<x2; ++x)
+ {
+ *sp++ = *bp; *sp++ = *bp; *sp++ = *bp; *sp++ = *bp;
+ *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp; *sp2++ = *bp;
+ *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp; *sp3++ = *bp;
+ *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp; *sp4++ = *bp;
+ ++bp;
+ }
+ screenp += multi_pitch;
+ screenp2 += multi_pitch;
+ screenp3 += multi_pitch;
+ screenp4 += multi_pitch;
+ bufp += SCREENWIDTH;
+ }
+}
+
+// Search through the given palette, finding the nearest color that matches
+// the given color.
+
+static int FindNearestColor(byte *palette, int r, int g, int b)
+{
+ byte *col;
+ int best;
+ int best_diff;
+ int diff;
+ int i;
+
+ best = 0;
+ best_diff = INT_MAX;
+
+ for (i=0; i<256; ++i)
+ {
+ col = palette + i * 3;
+ diff = (r - col[0]) * (r - col[0])
+ + (g - col[1]) * (g - col[1])
+ + (b - col[2]) * (b - col[2]);
+
+ if (diff == 0)
+ {
+ return i;
+ }
+ else if (diff < best_diff)
+ {
+ best = i;
+ best_diff = diff;
+ }
+ }
+
+ return best;
+}
+
+// Create a stretch table. This is a lookup table for blending colors.
+// pct specifies the bias between the two colors: 0 = all y, 100 = all x.
+// NB: This is identical to the lookup tables used in other ports for
+// translucency.
+
+static byte *GenerateStretchTable(byte *palette, int pct)
+{
+ byte *result;
+ int x, y;
+ int r, g, b;
+ byte *col1;
+ byte *col2;
+
+ result = Z_Malloc(256 * 256, PU_STATIC, NULL);
+
+ for (x=0; x<256; ++x)
+ {
+ for (y=0; y<256; ++y)
+ {
+ col1 = palette + x * 3;
+ col2 = palette + y * 3;
+ r = (((int) col1[0]) * pct + ((int) col2[0]) * (100 - pct)) / 100;
+ g = (((int) col1[1]) * pct + ((int) col2[1]) * (100 - pct)) / 100;
+ b = (((int) col1[2]) * pct + ((int) col2[2]) * (100 - pct)) / 100;
+ result[x * 256 + y] = FindNearestColor(palette, r, g, b);
+ }
+ }
+
+ return result;
+}
+
+void I_InitStretchTables(byte *palette)
+{
+ // We only actually need two lookup tables:
+ //
+ // mix 0% = just write line 1
+ // mix 20% = stretch_tables[0]
+ // mix 40% = stretch_tables[1]
+ // mix 60% = stretch_tables[1] used backwards
+ // mix 80% = stretch_tables[0] used backwards
+ // mix 100% = just write line 2
+
+ printf("I_InitStretchTables: Generating lookup tables..");
+ fflush(stdout);
+ stretch_tables[0] = GenerateStretchTable(palette, 20);
+ printf(".."); fflush(stdout);
+ stretch_tables[1] = GenerateStretchTable(palette, 40);
+ puts("");
+}
+
+static void WriteBlendedLine1x(byte *dest, byte *src1, byte *src2,
+ byte *stretch_table)
+{
+ int x;
+
+ for (x=0; x<SCREENWIDTH; ++x)
+ {
+ *dest = stretch_table[*src1 * 256 + *src2];
+ ++dest;
+ ++src1;
+ ++src2;
+ }
+}
+
+void I_Stretch1x(int x1, int y1, int x2, int y2)
+{
+ byte *bufp, *screenp;
+ int y;
+
+ // Only works with full screen update
+
+ if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)
+ {
+ return;
+ }
+
+ // Need to byte-copy from buffer into the screen buffer
+
+ bufp = src_buffer + y1 * SCREENWIDTH + x1;
+ screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;
+
+ // For every 5 lines of src_buffer, 6 lines are written to dest_buffer
+ // (200 -> 240)
+
+ for (y=0; y<SCREENHEIGHT; y += 5)
+ {
+ // 100% line 0
+ memcpy(screenp, bufp, SCREENWIDTH);
+ screenp += dest_pitch;
+
+ // 20% line 0, 80% line 1
+ WriteBlendedLine1x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[0]);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+
+ // 40% line 1, 60% line 2
+ WriteBlendedLine1x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[1]);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+
+ // 60% line 2, 40% line 3
+ WriteBlendedLine1x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[1]);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+
+ // 80% line 3, 20% line 4
+ WriteBlendedLine1x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[0]);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+
+ // 100% line 4
+ memcpy(screenp, bufp, SCREENWIDTH);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+ }
+}
+
+static void WriteLine2x(byte *dest, byte *src)
+{
+ int x;
+
+ for (x=0; x<SCREENWIDTH; ++x)
+ {
+ dest[0] = *src;
+ dest[1] = *src;
+ dest += 2;
+ ++src;
+ }
+}
+
+static void WriteBlendedLine2x(byte *dest, byte *src1, byte *src2,
+ byte *stretch_table)
+{
+ int x;
+ int val;
+
+ for (x=0; x<SCREENWIDTH; ++x)
+ {
+ val = stretch_table[*src1 * 256 + *src2];
+ dest[0] = val;
+ dest[1] = val;
+ dest += 2;
+ ++src1;
+ ++src2;
+ }
+}
+
+// Scale 2x, correcting aspect ratio in the process
+
+void I_Stretch2x(int x1, int y1, int x2, int y2)
+{
+ byte *bufp, *screenp;
+ int y;
+
+ // Only works with full screen update
+
+ if (x1 != 0 || y1 != 0 || x2 != SCREENWIDTH || y2 != SCREENHEIGHT)
+ {
+ return;
+ }
+
+ // Need to byte-copy from buffer into the screen buffer
+
+ bufp = src_buffer + y1 * SCREENWIDTH + x1;
+ screenp = (byte *) dest_buffer + y1 * dest_pitch + x1;
+
+ // For every 5 lines of src_buffer, 12 lines are written to dest_buffer.
+ // (200 -> 480)
+
+ for (y=0; y<SCREENHEIGHT; y += 5)
+ {
+ // 100% line 0
+ WriteLine2x(screenp, bufp);
+ screenp += dest_pitch;
+
+ // 100% line 0
+ WriteLine2x(screenp, bufp);
+ screenp += dest_pitch;
+
+ // 40% line 0, 60% line 1
+ WriteBlendedLine2x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[1]);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+
+ // 100% line 1
+ WriteLine2x(screenp, bufp);
+ screenp += dest_pitch;
+
+ // 80% line 1, 20% line 2
+ WriteBlendedLine2x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[0]);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+
+ // 100% line 2
+ WriteLine2x(screenp, bufp);
+ screenp += dest_pitch;
+
+ // 100% line 2
+ WriteLine2x(screenp, bufp);
+ screenp += dest_pitch;
+
+ // 20% line 2, 80% line 3
+ WriteBlendedLine2x(screenp, bufp, bufp + SCREENWIDTH, stretch_tables[0]);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+
+ // 100% line 3
+ WriteLine2x(screenp, bufp);
+ screenp += dest_pitch;
+
+ // 60% line 3, 40% line 4
+ WriteBlendedLine2x(screenp, bufp + SCREENWIDTH, bufp, stretch_tables[1]);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+
+ // 100% line 4
+ WriteLine2x(screenp, bufp);
+ screenp += dest_pitch;
+
+ // 100% line 4
+ WriteLine2x(screenp, bufp);
+ screenp += dest_pitch; bufp += SCREENWIDTH;
+ }
+}
+