1 /* Emacs style mode select -*- C++ -*-
2 *-----------------------------------------------------------------------------
3 *
4 *
5 * PrBoom: a Doom port merged with LxDoom and LSDLDoom
6 * based on BOOM, a modified and improved DOOM engine
7 * Copyright (C) 1999 by
8 * id Software, Chi Hoang, Lee Killough, Jim Flynn, Rand Phares, Ty Halderman
9 * Copyright (C) 1999-2000 by
10 * Jess Haas, Nicolas Kalkhof, Colin Phipps, Florian Schulze
11 * Copyright 2005, 2006 by
12 * Florian Schulze, Colin Phipps, Neil Stevens, Andrey Budko
13 *
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
27 * 02111-1307, USA.
28 *
29 * DESCRIPTION:
30 *
31 *---------------------------------------------------------------------
32 */
33
34 /*
35 ** gl_hqresize.cpp
36 ** Contains high quality upsampling functions.
37 ** So far Scale2x/3x/4x as described in http://scale2x.sourceforge.net/
38 ** are implemented.
39 **
40 **---------------------------------------------------------------------------
41 ** Copyright 2008 Benjamin Berkels
42 ** All rights reserved.
43 **
44 ** Redistribution and use in source and binary forms, with or without
45 ** modification, are permitted provided that the following conditions
46 ** are met:
47 **
48 ** 1. Redistributions of source code must retain the above copyright
49 ** notice, this list of conditions and the following disclaimer.
50 ** 2. Redistributions in binary form must reproduce the above copyright
51 ** notice, this list of conditions and the following disclaimer in the
52 ** documentation and/or other materials provided with the distribution.
53 ** 3. The name of the author may not be used to endorse or promote products
54 ** derived from this software without specific prior written permission.
55 **
56 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
57 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
58 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
59 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
60 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
61 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
62 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
63 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
64 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
65 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
66 **---------------------------------------------------------------------------
67 **
68 */
69
70 #ifdef HAVE_CONFIG_H
71 #include "config.h"
72 #endif
73
74 #include <SDL.h>
75
76 #include "doomstat.h"
77 #include "v_video.h"
78 #include "gl_intern.h"
79
80 int gl_texture_hqresize;
81 const char *gl_hqresizemodes[hq_scale_max] = {
82 "Off", "Scale2x", "Scale3x", "Scale4x"};
83
84 int gl_texture_hqresize;
85 int gl_texture_hqresize_maxinputsize = 512;
86 int gl_texture_hqresize_textures;
87 int gl_texture_hqresize_sprites;
88 int gl_texture_hqresize_patches;
89
scale2x(unsigned int * inputBuffer,unsigned int * outputBuffer,int inWidth,int inHeight,int seamlessWidth,int seamlessHeight)90 static void scale2x ( unsigned int* inputBuffer, unsigned int* outputBuffer, int inWidth, int inHeight, int seamlessWidth, int seamlessHeight )
91 {
92 int i, j;
93 const int width = 2* inWidth;
94 //const int height = 2 * inHeight;
95
96 for ( i = 0; i < inWidth; ++i )
97 {
98 // [JB] when the current index is at an edge and seamlessWidth is true,
99 // the opposite edge's index will be used for iMinus and iPlus
100 // when the current index is at an edge and seamlessWidth is false,
101 // the current index will be used for iMinus and iPlus
102 // otherwise iMinus and iPlus are equal to i-1 and i+1 respectively
103 const int iMinus = ((i == 0) ? (seamlessWidth ? inWidth - 1 : 0) : (i - 1));
104 const int iPlus = ((i == inWidth - 1) ? (seamlessWidth ? 0 : i) : (i + 1));
105 for ( j = 0; j < inHeight; ++j )
106 {
107 const int jMinus = ((j == 0) ? (seamlessHeight ? inHeight - 1 : 0) : (j - 1));
108 const int jPlus = ((j == inHeight - 1) ? (seamlessHeight ? 0 : j) : (j + 1));
109 //const unsigned int A = inputBuffer[ iMinus +inWidth*jMinus];
110 const unsigned int B = inputBuffer[ iMinus +inWidth*j ];
111 //const unsigned int C = inputBuffer[ iMinus +inWidth*jPlus];
112 const unsigned int D = inputBuffer[ i +inWidth*jMinus];
113 const unsigned int E = inputBuffer[ i +inWidth*j ];
114 const unsigned int F = inputBuffer[ i +inWidth*jPlus];
115 //const unsigned int G = inputBuffer[ iPlus +inWidth*jMinus];
116 const unsigned int H = inputBuffer[ iPlus +inWidth*j ];
117 //const unsigned int I = inputBuffer[ iPlus +inWidth*jPlus];
118 if (B != H && D != F) {
119 outputBuffer[2*i + width*2*j ] = D == B ? D : E;
120 outputBuffer[2*i + width*(2*j+1)] = B == F ? F : E;
121 outputBuffer[2*i+1 + width*2*j ] = D == H ? D : E;
122 outputBuffer[2*i+1 + width*(2*j+1)] = H == F ? F : E;
123 } else {
124 outputBuffer[2*i + width*2*j ] = E;
125 outputBuffer[2*i + width*(2*j+1)] = E;
126 outputBuffer[2*i+1 + width*2*j ] = E;
127 outputBuffer[2*i+1 + width*(2*j+1)] = E;
128 }
129 }
130 }
131 }
132
scale3x(unsigned int * inputBuffer,unsigned int * outputBuffer,int inWidth,int inHeight,int seamlessWidth,int seamlessHeight)133 static void scale3x ( unsigned int* inputBuffer, unsigned int* outputBuffer, int inWidth, int inHeight, int seamlessWidth, int seamlessHeight )
134 {
135 int i, j;
136 const int width = 3* inWidth;
137 //const int height = 3 * inHeight;
138
139 for ( i = 0; i < inWidth; ++i )
140 {
141 const int iMinus = ((i == 0) ? (seamlessWidth ? inWidth - 1 : 0) : (i - 1));
142 const int iPlus = ((i == inWidth - 1) ? (seamlessWidth ? 0 : i) : (i + 1));
143 for ( j = 0; j < inHeight; ++j )
144 {
145 const int jMinus = ((j == 0) ? (seamlessHeight ? inHeight - 1 : 0) : (j - 1));
146 const int jPlus = ((j == inHeight - 1) ? (seamlessHeight ? 0 : j) : (j + 1));
147 const unsigned int A = inputBuffer[ iMinus +inWidth*jMinus];
148 const unsigned int B = inputBuffer[ iMinus +inWidth*j ];
149 const unsigned int C = inputBuffer[ iMinus +inWidth*jPlus];
150 const unsigned int D = inputBuffer[ i +inWidth*jMinus];
151 const unsigned int E = inputBuffer[ i +inWidth*j ];
152 const unsigned int F = inputBuffer[ i +inWidth*jPlus];
153 const unsigned int G = inputBuffer[ iPlus +inWidth*jMinus];
154 const unsigned int H = inputBuffer[ iPlus +inWidth*j ];
155 const unsigned int I = inputBuffer[ iPlus +inWidth*jPlus];
156 if (B != H && D != F) {
157 outputBuffer[3*i + width*3*j ] = D == B ? D : E;
158 outputBuffer[3*i + width*(3*j+1)] = (D == B && E != C) || (B == F && E != A) ? B : E;
159 outputBuffer[3*i + width*(3*j+2)] = B == F ? F : E;
160 outputBuffer[3*i+1 + width*3*j ] = (D == B && E != G) || (D == H && E != A) ? D : E;
161 outputBuffer[3*i+1 + width*(3*j+1)] = E;
162 outputBuffer[3*i+1 + width*(3*j+2)] = (B == F && E != I) || (H == F && E != C) ? F : E;
163 outputBuffer[3*i+2 + width*3*j ] = D == H ? D : E;
164 outputBuffer[3*i+2 + width*(3*j+1)] = (D == H && E != I) || (H == F && E != G) ? H : E;
165 outputBuffer[3*i+2 + width*(3*j+2)] = H == F ? F : E;
166 } else {
167 outputBuffer[3*i + width*3*j ] = E;
168 outputBuffer[3*i + width*(3*j+1)] = E;
169 outputBuffer[3*i + width*(3*j+2)] = E;
170 outputBuffer[3*i+1 + width*3*j ] = E;
171 outputBuffer[3*i+1 + width*(3*j+1)] = E;
172 outputBuffer[3*i+1 + width*(3*j+2)] = E;
173 outputBuffer[3*i+2 + width*3*j ] = E;
174 outputBuffer[3*i+2 + width*(3*j+1)] = E;
175 outputBuffer[3*i+2 + width*(3*j+2)] = E;
176 }
177 }
178 }
179 }
180
scale4x(unsigned int * inputBuffer,unsigned int * outputBuffer,int inWidth,int inHeight,int seamlessWidth,int seamlessHeight)181 static void scale4x ( unsigned int* inputBuffer, unsigned int* outputBuffer, int inWidth, int inHeight, int seamlessWidth, int seamlessHeight )
182 {
183 unsigned int * buffer2x = malloc((2 * inWidth) * (2 * inHeight) * sizeof(unsigned int));
184 scale2x (inputBuffer, buffer2x, inWidth, inHeight, seamlessWidth, seamlessHeight);
185 scale2x (buffer2x, outputBuffer, 2 * inWidth, 2 * inHeight, seamlessWidth, seamlessHeight);
186 free(buffer2x);
187 }
188
189
HQScaleHelper(void (* scaleNxFunction)(unsigned int *,unsigned int *,int,int,int,int),const int N,unsigned char * inputBuffer,const int inWidth,const int inHeight,int * outWidth,int * outHeight,int seamlessWidth,int seamlessHeight)190 static unsigned char *HQScaleHelper( void (*scaleNxFunction) ( unsigned int* , unsigned int* , int , int, int, int),
191 const int N,
192 unsigned char *inputBuffer,
193 const int inWidth,
194 const int inHeight,
195 int *outWidth,
196 int *outHeight,
197 int seamlessWidth,
198 int seamlessHeight )
199 {
200 unsigned char * newBuffer;
201
202 (*outWidth) = N * inWidth;
203 (*outHeight) = N *inHeight;
204 newBuffer = malloc((*outWidth) * (*outHeight) * 4 * sizeof(unsigned char));
205
206 scaleNxFunction ( (unsigned int*)inputBuffer, (unsigned int*)newBuffer, inWidth, inHeight, seamlessWidth, seamlessHeight );
207 free(inputBuffer);
208 inputBuffer = NULL;
209 return newBuffer;
210 }
211
212 //===========================================================================
213 //
214 // [BB] Upsamples the texture in inputBuffer, frees inputBuffer and returns
215 // the upsampled buffer.
216 //
217 //===========================================================================
gld_HQResize(GLTexture * gltexture,unsigned char * inputBuffer,int inWidth,int inHeight,int * outWidth,int * outHeight)218 unsigned char* gld_HQResize(GLTexture *gltexture, unsigned char *inputBuffer, int inWidth, int inHeight, int *outWidth, int *outHeight)
219 {
220 int w = inWidth;
221 int h = inHeight;
222 unsigned char *result = inputBuffer;
223 gl_hqresizemode_t scale_mode = hq_scale_none;
224 int sw = 0;
225 int sh = 0;
226
227 if (outWidth) *outWidth = inWidth;
228 if (outHeight) *outHeight = inHeight;
229
230 if (!gl_texture_hqresize || !gltexture || !inputBuffer)
231 return result;
232
233 // [BB] Don't resample if the width or height of the input texture is bigger than gl_texture_hqresize_maxinputsize.
234 if ((inWidth > gl_texture_hqresize_maxinputsize) ||
235 (inHeight > gl_texture_hqresize_maxinputsize))
236 return result;
237
238 // [BB] The hqnx upsampling (not the scaleN one) destroys partial transparency, don't upsamle textures using it.
239 //if (gltexture->bIsTransparent == 1)
240 // return inputBuffer;
241
242 switch (gltexture->textype)
243 {
244 case GLDT_PATCH:
245 sw = sh = 0;
246 if (gltexture->flags & GLTEXTURE_SPRITE)
247 scale_mode = gl_texture_hqresize_sprites;
248 else
249 scale_mode = gl_texture_hqresize_patches;
250 break;
251
252 case GLDT_FLAT:
253 sw = sh = 1;
254 scale_mode = gl_texture_hqresize_textures;
255 break;
256
257 case GLDT_TEXTURE:
258 //sw = gltexture->flags & GLTEXTURE_SKY;
259 //sh = 0;
260 sw = sh = 1;
261 scale_mode = gl_texture_hqresize_textures;
262 break;
263 }
264
265 switch (scale_mode)
266 {
267 case hq_scale_2x:
268 result = HQScaleHelper(&scale2x, 2, inputBuffer, inWidth, inHeight, &w, &h, sw, sh);
269 break;
270 case hq_scale_3x:
271 result = HQScaleHelper(&scale3x, 3, inputBuffer, inWidth, inHeight, &w, &h, sw, sh);
272 break;
273 case hq_scale_4x:
274 result = HQScaleHelper(&scale4x, 4, inputBuffer, inWidth, inHeight, &w, &h, sw, sh);
275 break;
276 }
277
278 if (result != inputBuffer)
279 {
280 gltexture->tex_width = w;
281 gltexture->tex_height = h;
282
283 if (outWidth) *outWidth = w;
284 if (outHeight) *outHeight = h;
285 }
286
287 return result;
288 }
289