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