Thomas Buck 10 лет назад
Родитель
Сommit
6eb0b47437

+ 6
- 0
ChangeLog.md Просмотреть файл

@@ -4,6 +4,12 @@
4 4
 
5 5
     [ 20140622 ]
6 6
     * Wrote simple image reader/writer utilizing libpng
7
+    * Created utils/pixels for pixel handling utilities
8
+    * Renamed Texture to TextureManager
9
+    * Added support for BGR(A) color types in TextureManager
10
+    * Added pngCheck() and pcxCheck() methods
11
+    * Added ColorMode and bpp info to pcx/png API
12
+    * Modified tga to match png/pcx API
7 13
 
8 14
     [ 20140621 ]
9 15
     * Created StaticMesh class replacing model_mesh_t stuff

+ 2
- 2
include/Render.h Просмотреть файл

@@ -12,7 +12,7 @@
12 12
 #include <vector>
13 13
 
14 14
 #include "Room.h"
15
-#include "Texture.h"
15
+#include "TextureManager.h"
16 16
 #include "ViewVolume.h"
17 17
 
18 18
 /*!
@@ -140,7 +140,7 @@ public:
140 140
 
141 141
     //! \fixme should be private
142 142
     ViewVolume mViewVolume; //!< View Volume for frustum culling
143
-    Texture mTexture; //!< Texture subsystem
143
+    TextureManager mTexture; //!< Texture subsystem
144 144
 
145 145
 private:
146 146
 

include/Texture.h → include/TextureManager.h Просмотреть файл

@@ -1,18 +1,18 @@
1 1
 /*!
2
- * \file include/Texture.h
2
+ * \file include/TextureManager.h
3 3
  * \brief Texture registry
4 4
  *
5 5
  * \author Mongoose
6 6
  * \author xythobuz
7 7
  */
8 8
 
9
-#ifndef _TEXTURE_H
10
-#define _TEXTURE_H
9
+#ifndef _TEXTURE_MANAGER_H
10
+#define _TEXTURE_MANAGER_H
11 11
 
12 12
 /*!
13 13
  * \brief Texture registry
14 14
  */
15
-class Texture {
15
+class TextureManager {
16 16
 public:
17 17
 
18 18
     enum ColorMode {
@@ -32,12 +32,12 @@ public:
32 32
     /*!
33 33
     * \brief Constructs an object of Texture
34 34
     */
35
-    Texture();
35
+    TextureManager();
36 36
 
37 37
    /*!
38 38
     * \brief Deconstructs an object of Texture
39 39
     */
40
-    ~Texture();
40
+    ~TextureManager();
41 41
 
42 42
     /*!
43 43
      * \brief Resets all texture data
@@ -148,12 +148,6 @@ public:
148 148
     void useMultiTexture(float aU, float aV, float bU, float bV);
149 149
 
150 150
 private:
151
-
152
-    int nextPower(int seed);
153
-
154
-    unsigned char *scaleBuffer(unsigned char *image, int width, int height,
155
-                                int components);
156
-
157 151
     unsigned int *mTextureIds;  //!< GL texture list
158 152
     unsigned int mTextureCount; //!< Texture counter
159 153
     unsigned int mTextureLimit; //!< The texture limit

+ 5
- 0
include/global.h Просмотреть файл

@@ -20,6 +20,11 @@
20 20
 #define __attribute__(x)
21 21
 #endif
22 22
 
23
+//! \todo Replace NULL usage with nullptr
24
+#ifndef NULL
25
+#define NULL nullptr
26
+#endif
27
+
23 28
 // Globally include OpenGL header
24 29
 #ifdef __APPLE__
25 30
 #include <OpenGL/gl.h>

+ 14
- 3
include/utils/pcx.h Просмотреть файл

@@ -11,15 +11,26 @@
11 11
 #ifndef _UTILS_PCX_H_
12 12
 #define _UTILS_PCX_H_
13 13
 
14
+#include "TextureManager.h"
15
+
16
+/*!
17
+ * \brief Check if a file is a valid PCX image
18
+ * \param filename path of file to read
19
+ * \returns 0 on success
20
+ */
21
+int pcxCheck(const char *filename);
22
+
14 23
 /*!
15
- * \brief Load a PCX image file into a RGBA buffer
24
+ * \brief Load a PCX image file into a buffer
16 25
  * \param filename path of file to read
17
- * \param image place where allocated buffer of size (width * height * 4) will be allocated
26
+ * \param image place where allocated buffer of size (width * height * (bpp / 8)) will be allocated
18 27
  * \param width place where image width will be stored
19 28
  * \param height place where image height will be stored
29
+ * \param mode place where Color Mode of image will be stored
30
+ * \param bpp place where pixel width will be stored (8, 24, 32)
20 31
  * \returns 0 on success
21 32
  */
22
-int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height);
33
+int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height, TextureManager::ColorMode *mode, unsigned int *bpp);
23 34
 
24 35
 #endif
25 36
 

+ 4
- 0
include/utils/pixel.h Просмотреть файл

@@ -8,7 +8,11 @@
8 8
 #ifndef _UTILS_PIXEL_H_
9 9
 #define _UTILS_PIXEL_H_
10 10
 
11
+void bgr2rgb24(unsigned char *image, unsigned int w, unsigned int h);
12
+void bgra2rgba32(unsigned char *image, unsigned int w, unsigned int h);
11 13
 void argb2rgba32(unsigned char *image, unsigned int w, unsigned int h);
12 14
 
15
+unsigned char *scaleBuffer(unsigned char *image, int width, int height, int components);
16
+
13 17
 #endif
14 18
 

+ 15
- 2
include/utils/png.h Просмотреть файл

@@ -8,15 +8,26 @@
8 8
 #ifndef _UTILS_PNG_H_
9 9
 #define _UTILS_PNG_H_
10 10
 
11
+#include "TextureManager.h"
12
+
13
+/*!
14
+ * \brief Check if a file is a valid PNG image
15
+ * \param filename path of file to read
16
+ * \returns 0 on success
17
+ */
18
+int pngCheck(const char *filename);
19
+
11 20
 /*!
12 21
  * \brief Load a PNG image file into an RGBA buffer
13 22
  * \param filename path of file to read
14 23
  * \param image place where allocated buffer of size (width * height * 4) will be allocated
15 24
  * \param width place where image width will be stored
16 25
  * \param height place where image height will be stored
26
+ * \param mode place where Color Mode of image will be stored
27
+ * \param bpp place where pixel width will be stored (8, 24, 32)
17 28
  * \returns 0 on success
18 29
  */
19
-int pngLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height);
30
+int pngLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height, TextureManager::ColorMode *mode, unsigned int *bpp);
20 31
 
21 32
 /*!
22 33
  * \brief Create a PNG image file from an RGBA buffer
@@ -24,9 +35,11 @@ int pngLoad(const char *filename, unsigned char **image, unsigned int *width, un
24 35
  * \param image buffer of size (width * height * 4)
25 36
  * \param width image width
26 37
  * \param height image height
38
+ * \param mode color mode
39
+ * \param bpp bits per pixel (8, 24, 32)
27 40
  * \returns 0 on success
28 41
  */
29
-int pngSave(const char *filename, unsigned char *image, unsigned int width, unsigned int height);
42
+int pngSave(const char *filename, unsigned char *image, unsigned int width, unsigned int height, TextureManager::ColorMode mode, unsigned int bpp);
30 43
 
31 44
 #endif
32 45
 

+ 0
- 47
include/utils/tga.h Просмотреть файл

@@ -9,39 +9,6 @@
9 9
 #define _UTILS_TGA_H
10 10
 
11 11
 /*!
12
- * \brief Possible TGA image types
13
- */
14
-typedef enum {
15
-    TGA_TYPE__NO_DATA    = 0,
16
-    TGA_TYPE__MAPPED     = 1,
17
-    TGA_TYPE__COLOR      = 2,
18
-    TGA_TYPE__GREYSCALE  = 3,
19
-    TGA_TYPE__MAPPED_RLE = 9,
20
-    TGA_TYPE__COLOR_RLE  = 10
21
-    // TGA_TYPE__GREYSCALE_COMPRESSED  = 11,
22
-    // TGA_TYPE__COLOR_HUFFMAN_DELTA_RLE  = 32,
23
-    // TGA_TYPE__COLOR_HUFFMAN_DELTA_RLE_4PASS  = 33
24
-} tga_type_t;
25
-
26
-/*!
27
- * \brief Data structure representing TGA file header
28
- */
29
-typedef struct {
30
-    unsigned char comment_lenght;       //!< Number of bytes in comment
31
-    unsigned char colormap_type;        //!< 0 No colormap; 1 Has colormap
32
-    unsigned char image_type;           //!< 1 Colormapped; 2 Unmapped; 9 Colormapped RLE; 10 Unmapped RLE
33
-    unsigned short colormap_index;      //!< Index of first color map entry
34
-    unsigned short colormap_lenght;     //!< Number of color map entries
35
-    unsigned char colormap_bbp;         //!< 16, 24, or 32 bits per pixel format
36
-    unsigned short origin_x;            //!< X coor of lower-left corner
37
-    unsigned short origin_y;            //!< Y coor of lower-left corner
38
-    unsigned short width;               //!< Width in pixels
39
-    unsigned short height;              //!< Height in pixels
40
-    unsigned char bpp;                  //!< Number of bits in a pixel index
41
-    unsigned char desc_flags;           //!< Various magic bits
42
-} tga_t;
43
-
44
-/*!
45 12
  * \brief Check if a file is a valid TGA image
46 13
  * \param f file to be checked
47 14
  * \returns 0 if valid, else error condition
@@ -72,19 +39,5 @@ int tgaLoad(FILE *f, unsigned char **image,
72 39
 int tgaSave(FILE *f, unsigned char *image,
73 40
                 unsigned int width, unsigned int height, char type);
74 41
 
75
-/*!
76
- * \brief Save a pixel buffer into a file on disk
77
- * \param image pixmap to be stored
78
- * \param width width of pixmap/image
79
- * \param height height of pixmap/image
80
- * \param type tga type to use
81
- * \param s format string for file path/name
82
- * \returns 0 on success, else error condition
83
- */
84
-int tgaSaveFilename(unsigned char *image,
85
-                        unsigned int width, unsigned int height,
86
-                        char type, const char *s, ...)
87
-    __attribute__((format(printf, 5, 6)));
88
-
89 42
 #endif
90 43
 

+ 1
- 1
src/CMakeLists.txt Просмотреть файл

@@ -66,7 +66,7 @@ set (SRCS ${SRCS} "SkeletalModel.cpp")
66 66
 set (SRCS ${SRCS} "Sound.cpp")
67 67
 set (SRCS ${SRCS} "Sprite.cpp")
68 68
 set (SRCS ${SRCS} "StaticMesh.cpp")
69
-set (SRCS ${SRCS} "Texture.cpp")
69
+set (SRCS ${SRCS} "TextureManager.cpp")
70 70
 set (SRCS ${SRCS} "TombRaider.cpp")
71 71
 set (SRCS ${SRCS} "ViewVolume.cpp")
72 72
 set (SRCS ${SRCS} "Window.cpp")

+ 7
- 4
src/Render.cpp Просмотреть файл

@@ -65,8 +65,11 @@ void Render::screenShot(char *filenameBase)
65 65
     // Capture frame buffer
66 66
     glReadPixels(0, 0, getWindow().getWidth(), getWindow().getHeight(), GL_BGR_EXT, GL_UNSIGNED_BYTE, image);
67 67
 
68
-    tgaSaveFilename(image, getWindow().getWidth(), getWindow().getHeight(), 0, "%s", filename);
69
-    printf("Took screenshot '%s'.\n", filename);
68
+    FILE *f = fopen(filename, "wb");
69
+    if (f) {
70
+        tgaSave(f, image, getWindow().getWidth(), getWindow().getHeight(), 0);
71
+        printf("Took screenshot '%s'.\n", filename);
72
+    }
70 73
 
71 74
     delete [] filename;
72 75
     delete [] image;
@@ -82,7 +85,7 @@ void Render::loadTexture(unsigned char *image,
82 85
         unsigned int id)
83 86
 {
84 87
     glColor3fv(WHITE);
85
-    mTexture.loadBufferSlot(image, width, height, Texture::RGBA, 32, id);
88
+    mTexture.loadBufferSlot(image, width, height, TextureManager::RGBA, 32, id);
86 89
 }
87 90
 
88 91
 
@@ -95,7 +98,7 @@ int Render::initTextures(char *textureDir) {
95 98
     mTexture.setMaxTextureCount(128);  /* TR never needs more than 32 iirc
96 99
                                           However, color texturegen is a lot */
97 100
 
98
-    mTexture.setFlag(Texture::fUseMipmaps);
101
+    mTexture.setFlag(TextureManager::fUseMipmaps);
99 102
 
100 103
     color[0] = 0xff;
101 104
     color[1] = 0xff;

+ 1
- 1
src/StaticMesh.cpp Просмотреть файл

@@ -123,7 +123,7 @@ int setupTextureColor(float *colorf) {
123 123
         gColorTextureHACK.push_back(colorI);
124 124
         texture = getGame().getTextureOffset() + gColorTextureHACK.size();
125 125
 
126
-        getRender().loadTexture(Texture::generateColorTexture(color, 32, 32),
126
+        getRender().loadTexture(TextureManager::generateColorTexture(color, 32, 32),
127 127
                 32, 32, texture);
128 128
     } else {
129 129
         texture = getGame().getTextureOffset() + foundIndex;

src/Texture.cpp → src/TextureManager.cpp Просмотреть файл

@@ -1,5 +1,5 @@
1 1
 /*!
2
- * \file src/Texture.cpp
2
+ * \file src/TextureManager.cpp
3 3
  * \brief Texture registry
4 4
  *
5 5
  * \author Mongoose
@@ -16,9 +16,9 @@
16 16
 #include "utils/pixel.h"
17 17
 #include "utils/strings.h"
18 18
 #include "utils/tga.h"
19
-#include "Texture.h"
19
+#include "TextureManager.h"
20 20
 
21
-Texture::Texture() {
21
+TextureManager::TextureManager() {
22 22
     mTextureIds = NULL;
23 23
     mFlags = 0;
24 24
     mTextureId = -1;
@@ -27,11 +27,11 @@ Texture::Texture() {
27 27
     mTextureLimit = 0;
28 28
 }
29 29
 
30
-Texture::~Texture() {
30
+TextureManager::~TextureManager() {
31 31
     reset();
32 32
 }
33 33
 
34
-unsigned char *Texture::generateColorTexture(unsigned char rgba[4],
34
+unsigned char *TextureManager::generateColorTexture(unsigned char rgba[4],
35 35
         unsigned int width, unsigned int height) {
36 36
     unsigned char *image;
37 37
     unsigned int i, size;
@@ -53,7 +53,7 @@ unsigned char *Texture::generateColorTexture(unsigned char rgba[4],
53 53
     return image;
54 54
 }
55 55
 
56
-int Texture::loadColorTexture(unsigned char rgba[4],
56
+int TextureManager::loadColorTexture(unsigned char rgba[4],
57 57
         unsigned int width, unsigned int height) {
58 58
     unsigned char *image;
59 59
     int id;
@@ -69,15 +69,15 @@ int Texture::loadColorTexture(unsigned char rgba[4],
69 69
     return id;
70 70
 }
71 71
 
72
-void Texture::setFlag(TextureFlag flag) {
72
+void TextureManager::setFlag(TextureFlag flag) {
73 73
     mFlags |= flag;
74 74
 }
75 75
 
76
-void Texture::clearFlag(TextureFlag flag) {
76
+void TextureManager::clearFlag(TextureFlag flag) {
77 77
     mFlags &= ~flag;
78 78
 }
79 79
 
80
-void Texture::reset() {
80
+void TextureManager::reset() {
81 81
     if (mTextureIds) {
82 82
         glDeleteTextures(mTextureLimit, mTextureIds);
83 83
         delete [] mTextureIds;
@@ -88,7 +88,7 @@ void Texture::reset() {
88 88
     mTextureLimit = 0;
89 89
 }
90 90
 
91
-void Texture::disableMultiTexture() {
91
+void TextureManager::disableMultiTexture() {
92 92
     mFlags ^= fUseMultiTexture;
93 93
     mTextureId = -1;
94 94
     mTextureId2 = -1;
@@ -97,7 +97,7 @@ void Texture::disableMultiTexture() {
97 97
     glActiveTextureARB(GL_TEXTURE0_ARB);
98 98
 }
99 99
 
100
-void Texture::useMultiTexture(float aU, float aV, float bU, float bV) {
100
+void TextureManager::useMultiTexture(float aU, float aV, float bU, float bV) {
101 101
     if (!(mFlags & fUseMultiTexture))
102 102
         return;
103 103
 
@@ -105,7 +105,7 @@ void Texture::useMultiTexture(float aU, float aV, float bU, float bV) {
105 105
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, bU, bV);
106 106
 }
107 107
 
108
-void Texture::useMultiTexture(float u, float v) {
108
+void TextureManager::useMultiTexture(float u, float v) {
109 109
     if (!(mFlags & fUseMultiTexture))
110 110
         return;
111 111
 
@@ -113,7 +113,7 @@ void Texture::useMultiTexture(float u, float v) {
113 113
     glMultiTexCoord2fARB(GL_TEXTURE1_ARB, u, v);
114 114
 }
115 115
 
116
-void Texture::bindMultiTexture(int texture0, int texture1) {
116
+void TextureManager::bindMultiTexture(int texture0, int texture1) {
117 117
     assert(mTextureIds != NULL);
118 118
     assert(texture0 >= 0);
119 119
     assert((unsigned int)texture0 <= mTextureCount);
@@ -133,7 +133,7 @@ void Texture::bindMultiTexture(int texture0, int texture1) {
133 133
     glBindTexture(GL_TEXTURE_2D, mTextureIds[texture1]);
134 134
 }
135 135
 
136
-void Texture::setMaxTextureCount(unsigned int n) {
136
+void TextureManager::setMaxTextureCount(unsigned int n) {
137 137
     assert(n > 0);
138 138
 
139 139
     mTextureLimit = n;
@@ -143,11 +143,11 @@ void Texture::setMaxTextureCount(unsigned int n) {
143 143
     glGenTextures(n, mTextureIds);
144 144
 }
145 145
 
146
-int Texture::getTextureCount() {
146
+int TextureManager::getTextureCount() {
147 147
     return mTextureCount - 1;
148 148
 }
149 149
 
150
-int Texture::loadBuffer(unsigned char *image,
150
+int TextureManager::loadBuffer(unsigned char *image,
151 151
         unsigned int width, unsigned int height,
152 152
         ColorMode mode, unsigned int bpp) {
153 153
     int id;
@@ -165,7 +165,7 @@ int Texture::loadBuffer(unsigned char *image,
165 165
     return id;
166 166
 }
167 167
 
168
-int Texture::loadBufferSlot(unsigned char *image,
168
+int TextureManager::loadBufferSlot(unsigned char *image,
169 169
         unsigned int width, unsigned int height,
170 170
         ColorMode mode, unsigned int bpp,
171 171
         unsigned int slot) {
@@ -182,7 +182,7 @@ int Texture::loadBufferSlot(unsigned char *image,
182 182
     switch (mode) {
183 183
         case GREYSCALE:
184 184
             if (bpp != 8) {
185
-                printf("Texture::Load ERROR Unsupported GREYSCALE, %i bpp\n", bpp);
185
+                printf("TextureManager::Load ERROR Unsupported GREYSCALE, %i bpp\n", bpp);
186 186
                 return -1;
187 187
             }
188 188
             bytes = 1;
@@ -191,7 +191,7 @@ int Texture::loadBufferSlot(unsigned char *image,
191 191
 
192 192
         case RGB:
193 193
             if (bpp != 24) {
194
-                printf("Texture::Load ERROR Unsupported RGB, %i bpp\n", bpp);
194
+                printf("TextureManager::Load ERROR Unsupported RGB, %i bpp\n", bpp);
195 195
                 return -1;
196 196
             }
197 197
             bytes = 3;
@@ -202,7 +202,7 @@ int Texture::loadBufferSlot(unsigned char *image,
202 202
             if (bpp == 32) {
203 203
                 argb2rgba32(image, width, height);
204 204
             } else {
205
-                printf("Texture::Load ERROR Unsupported ARGB, %i bpp\n", bpp);
205
+                printf("TextureManager::Load ERROR Unsupported ARGB, %i bpp\n", bpp);
206 206
                 return -1;
207 207
             }
208 208
             bytes = 4;
@@ -211,7 +211,7 @@ int Texture::loadBufferSlot(unsigned char *image,
211 211
 
212 212
         case RGBA:
213 213
             if (bpp != 32) {
214
-                printf("Texture::Load ERROR Unsupported RGBA, %i bpp\n", bpp);
214
+                printf("TextureManager::Load ERROR Unsupported RGBA, %i bpp\n", bpp);
215 215
                 return -1;
216 216
             }
217 217
             bytes = 4;
@@ -220,7 +220,7 @@ int Texture::loadBufferSlot(unsigned char *image,
220 220
 
221 221
         case BGR:
222 222
             if (bpp != 24) {
223
-                printf("Texture::Load ERROR Unsupported BGR, %i bpp\n", bpp);
223
+                printf("TextureManager::Load ERROR Unsupported BGR, %i bpp\n", bpp);
224 224
                 return -1;
225 225
             }
226 226
             bytes = 3;
@@ -229,7 +229,7 @@ int Texture::loadBufferSlot(unsigned char *image,
229 229
 
230 230
         case BGRA:
231 231
             if (bpp != 32) {
232
-                printf("Texture::Load ERROR Unsupported BGRA, %i bpp\n", bpp);
232
+                printf("TextureManager::Load ERROR Unsupported BGRA, %i bpp\n", bpp);
233 233
                 return -1;
234 234
             }
235 235
             bytes = 4;
@@ -270,7 +270,7 @@ int Texture::loadBufferSlot(unsigned char *image,
270 270
     return slot;
271 271
 }
272 272
 
273
-void Texture::bindTextureId(unsigned int n) {
273
+void TextureManager::bindTextureId(unsigned int n) {
274 274
     assert(mTextureIds != NULL);
275 275
     assert(n <= mTextureCount);
276 276
 
@@ -282,19 +282,20 @@ void Texture::bindTextureId(unsigned int n) {
282 282
     glBindTexture(GL_TEXTURE_2D, mTextureIds[n]);
283 283
 }
284 284
 
285
-int Texture::loadPCX(const char *filename) {
285
+int TextureManager::loadPCX(const char *filename) {
286 286
     unsigned char *image;
287
-    unsigned int w, h;
287
+    unsigned int w, h, bpp;
288
+    ColorMode c;
288 289
     int id = -1;
289
-    int error = pcxLoad(filename, &image, &w, &h);
290
+    int error = pcxLoad(filename, &image, &w, &h, &c, &bpp);
290 291
     if (error == 0) {
291
-        id = loadBuffer(image, w, h, RGBA, 32);
292
+        id = loadBuffer(image, w, h, c, bpp);
292 293
         delete [] image;
293 294
     }
294 295
     return id;
295 296
 }
296 297
 
297
-int Texture::loadTGA(const char *filename) {
298
+int TextureManager::loadTGA(const char *filename) {
298 299
     FILE *f;
299 300
     unsigned char *image = NULL;
300 301
     unsigned char *image2 = NULL;
@@ -317,6 +318,7 @@ int Texture::loadTGA(const char *filename) {
317 318
         image2 = scaleBuffer(image, w, h, (type == 4) ? 4 : 3);
318 319
 
319 320
         if (image2) {
321
+            delete [] image;
320 322
             image = image2;
321 323
             w = h = 256;
322 324
         }
@@ -333,156 +335,9 @@ int Texture::loadTGA(const char *filename) {
333 335
     }
334 336
 
335 337
     if (id == -1) {
336
-        printf("Texture::loadTGA> ERROR: Failed to load '%s'\n", filename);
338
+        printf("TextureManager::loadTGA> ERROR: Failed to load '%s'\n", filename);
337 339
     }
338 340
 
339 341
     return id;
340 342
 }
341 343
 
342
-int Texture::nextPower(int seed) {
343
-    int i = 1;
344
-    for (; i < seed; i *= 2);
345
-    return i;
346
-}
347
-
348
-/* This code based off on gluScaleImage()  */
349
-unsigned char *Texture::scaleBuffer(unsigned char *image,
350
-        int width, int height, int components) {
351
-    int i, j, k;
352
-    float* tempin;
353
-    float* tempout;
354
-    float sx, sy;
355
-    //int components = 3;
356
-    unsigned char *timage;
357
-    int original_height = height;
358
-    int original_width = width;
359
-
360
-    assert(image != NULL);
361
-    assert(width > 0);
362
-    assert(height > 0);
363
-
364
-    height = nextPower(height);
365
-    width = nextPower(width);
366
-
367
-    if (height > 256)
368
-        height = 256;
369
-
370
-    if (width > 256)
371
-        width = 256;
372
-
373
-    // Check to see if scaling is needed
374
-    if (height == original_height && width == original_width)
375
-        return NULL;
376
-
377
-    //printf("%i\n", components);
378
-
379
-    timage = new unsigned char[height * width * components];
380
-    tempin = new float[original_width * original_height * components * sizeof(float)];
381
-    tempout = new float[width * height * components * sizeof(float)];
382
-
383
-    // Copy user data to float format.
384
-    for (i = 0; i < original_height * original_width * components; ++i) {
385
-        tempin[i] = (float)image[i];
386
-    }
387
-
388
-    // Determine which filter to use by checking ratios.
389
-    if (width > 1) {
390
-        sx = (float)(original_width - 1) / (float)(width - 1);
391
-    } else {
392
-        sx = (float)(original_width - 1);
393
-    }
394
-
395
-    if (height > 1) {
396
-        sy = (float)(original_height - 1) / (float) (height - 1);
397
-    } else {
398
-        sy = (float)(original_height - 1);
399
-    }
400
-
401
-    if (sx < 1.0 && sy < 1.0) {
402
-        /* Magnify both width and height:  use weighted sample of 4 pixels */
403
-        for (i = 0; i < height; ++i) {
404
-            int i0 = (int)(i * sy);
405
-            int i1 = i0 + 1;
406
-
407
-            if (i1 >= original_height) {
408
-                i1 = original_height - 1;
409
-            }
410
-
411
-            float alpha = i * sy - i0;
412
-
413
-            for (j = 0; j < width; ++j) {
414
-                int j0 = (int) (j * sx);
415
-                int j1 = j0 + 1;
416
-
417
-                if (j1 >= original_width) {
418
-                    j1 = original_width - 1;
419
-                }
420
-
421
-                float beta = j * sx - j0;
422
-
423
-                /* Compute weighted average of pixels in rect (i0,j0)-(i1,j1) */
424
-                float *src00 = tempin + (i0 * original_width + j0) * components;
425
-                float *src01 = tempin + (i0 * original_width + j1) * components;
426
-                float *src10 = tempin + (i1 * original_width + j0) * components;
427
-                float *src11 = tempin + (i1 * original_width + j1) * components;
428
-
429
-                float *dst = tempout + (i * width + j) * components;
430
-
431
-                for (k = 0; k < components; ++k) {
432
-                    float s1 = *src00++ * (1.0f - beta) + *src01++ * beta;
433
-                    float s2 = *src10++ * (1.0f - beta) + *src11++ * beta;
434
-                    *dst++ = s1 * (1.0f - alpha) + s2 * alpha;
435
-                }
436
-            }
437
-        }
438
-    } else {
439
-        /* Shrink width and/or height:  use an unweighted box filter */
440
-        for (i = 0; i < height; ++i) {
441
-            int i0 = (int) (i * sy);
442
-            int i1 = i0 + 1;
443
-
444
-            if (i1 >= original_height) {
445
-                i1 = original_height - 1;
446
-            }
447
-
448
-            for (j = 0; j < width; ++j) {
449
-                int j0 = (int) (j * sx);
450
-                int j1 = j0 + 1;
451
-
452
-                if (j1 >= original_width) {
453
-                    j1 = original_width - 1;
454
-                }
455
-
456
-                float *dst = tempout + (i * width + j) * components;
457
-
458
-                /* Compute average of pixels in the rectangle (i0,j0)-(i1,j1) */
459
-                for (k = 0; k < components; ++k) {
460
-                    float sum = 0.0;
461
-
462
-                    for (int ii = i0; ii <= i1; ++ii) {
463
-                        for (int jj = j0; jj <= j1; ++jj) {
464
-                            sum += *(tempin + (ii * original_width + jj)
465
-                                    * components + k);
466
-                        }
467
-                    }
468
-
469
-                    sum /= ( j1 - j0 + 1 ) * ( i1 - i0 + 1 );
470
-                    *dst++ = sum;
471
-                }
472
-            }
473
-        }
474
-    }
475
-
476
-    // Copy to our results.
477
-    for (i = 0; i < height * width * components; ++i) {
478
-        timage[i] = (unsigned char)tempout[i];
479
-    }
480
-
481
-    // Delete our temp buffers.
482
-    delete[] tempin;
483
-    delete[] tempout;
484
-    delete[] image;
485
-
486
-    return timage;
487
-}
488
-

+ 63
- 2
src/utils/pcx.cpp Просмотреть файл

@@ -20,7 +20,66 @@
20 20
 void pcxPrint(...) { }
21 21
 #endif
22 22
 
23
-int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height) {
23
+int pcxCheck(const char *filename) {
24
+    assert(filename != NULL);
25
+    assert(filename[0] != '\0');
26
+
27
+    std::ifstream file(filename, std::ios::in | std::ios::binary);
28
+
29
+    // Read raw PCX header, 128 bytes
30
+    unsigned char *header = new unsigned char[128];
31
+
32
+    // Basic validation
33
+    if (!file.read((char *)(&header[0]), 128)) {
34
+        pcxPrint("File not big enough for valid PCX header!");
35
+        delete [] header;
36
+        return -1;
37
+    }
38
+
39
+    if (header[0] != 0x0A) {
40
+        pcxPrint("Magic number at file start is wrong (0x%X != 0x0A)", header[0]);
41
+        delete [] header;
42
+        return -2;
43
+    }
44
+
45
+    if ((header[1] != 0) && ((header[1] < 2) || (header[1] > 5))) {
46
+        // Valid: 0, 2, 3, 4, 5
47
+        pcxPrint("Unknown PCX file format version (%d)", header[1]);
48
+        delete [] header;
49
+        return -3;
50
+    }
51
+
52
+    if ((header[2] != 0) && (header[2] != 1)) {
53
+        pcxPrint("Unknown PCX file encoding (%d)", header[2]);
54
+        delete [] header;
55
+        return -4;
56
+    }
57
+
58
+    if (header[3] != 8) {
59
+        pcxPrint("Only supporting 8bit (%dbit)", header[3]);
60
+        delete [] header;
61
+        return -5;
62
+    }
63
+
64
+    if (header[64] != 0) {
65
+        pcxPrint("Reserved field is  used (%d != 0)", header[64]);
66
+        delete [] header;
67
+        return -6;
68
+    }
69
+
70
+    delete [] header;
71
+    return 0;
72
+}
73
+
74
+int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height, TextureManager::ColorMode *mode, unsigned int *bpp) {
75
+    assert(filename != NULL);
76
+    assert(filename[0] != '\0');
77
+    assert(image != NULL);
78
+    assert(width != NULL);
79
+    assert(height != NULL);
80
+    assert(mode != NULL);
81
+    assert(bpp != NULL);
82
+
24 83
     std::ifstream file(filename, std::ios::in | std::ios::binary);
25 84
 
26 85
     // Read raw PCX header, 128 bytes
@@ -185,9 +244,11 @@ int pcxLoad(const char *filename, unsigned char **image, unsigned int *width, un
185 244
         }
186 245
     }
187 246
 
247
+    *mode = TextureManager::RGBA;
248
+    *bpp = 32;
249
+
188 250
     delete [] buffer;
189 251
     delete [] palette;
190
-
191 252
     return 0;
192 253
 }
193 254
 

+ 164
- 0
src/utils/pixel.cpp Просмотреть файл

@@ -8,6 +8,32 @@
8 8
 #include "global.h"
9 9
 #include "utils/pixel.h"
10 10
 
11
+void bgr2rgb24(unsigned char *image, unsigned int w, unsigned int h) {
12
+    assert(image != nullptr);
13
+    assert(w > 0);
14
+    assert(h > 0);
15
+
16
+    for (unsigned int i = 0; i < (w * h); ++i) {
17
+        /* 24-bit BGR to RGB */
18
+        unsigned char swap = image[(i * 3) + 2];
19
+        image[(i * 3) + 2] = image[(i * 3)];
20
+        image[(i * 3)] = swap;
21
+    }
22
+}
23
+
24
+void bgra2rgba32(unsigned char *image, unsigned int w, unsigned int h) {
25
+    assert(image != nullptr);
26
+    assert(w > 0);
27
+    assert(h > 0);
28
+
29
+    for (unsigned int i = 0; i < (w * h); ++i) {
30
+        /* 32-bit BGRA to RGBA */
31
+        unsigned char swap = image[(i * 4) + 2];
32
+        image[(i * 4) + 2] = image[(i * 4)];
33
+        image[(i * 4)] = swap;
34
+    }
35
+}
36
+
11 37
 void argb2rgba32(unsigned char *image, unsigned int w, unsigned int h) {
12 38
     assert(image != nullptr);
13 39
     assert(w > 0);
@@ -23,3 +49,141 @@ void argb2rgba32(unsigned char *image, unsigned int w, unsigned int h) {
23 49
     }
24 50
 }
25 51
 
52
+#define NEXT_POWER(x) do {        \
53
+    int i;                        \
54
+    for (i = 1; i < (x); i *= 2); \
55
+    x = i;                        \
56
+} while (false);
57
+
58
+// This code based off on gluScaleImage()
59
+unsigned char *scaleBuffer(unsigned char *image, int width, int height, int components) {
60
+    assert(image != NULL);
61
+    assert(width > 0);
62
+    assert(height > 0);
63
+    assert((components == 3) || (components == 4));
64
+
65
+    int original_height = height;
66
+    int original_width = width;
67
+
68
+    NEXT_POWER(height);
69
+    NEXT_POWER(width);
70
+
71
+    if (height > 256)
72
+        height = 256;
73
+
74
+    if (width > 256)
75
+        width = 256;
76
+
77
+    // Check to see if scaling is needed
78
+    if (height == original_height && width == original_width)
79
+        return NULL;
80
+
81
+    unsigned char *timage = new unsigned char[height * width * components];
82
+    float *tempin = new float[original_width * original_height * components];
83
+    float *tempout = new float[width * height * components];
84
+
85
+    // Copy user data to float format.
86
+    for (int i = 0; i < original_height * original_width * components; ++i) {
87
+        tempin[i] = (float)image[i];
88
+    }
89
+
90
+    // Determine which filter to use by checking ratios.
91
+    float sx;
92
+    if (width > 1) {
93
+        sx = (float)(original_width - 1) / (float)(width - 1);
94
+    } else {
95
+        sx = (float)(original_width - 1);
96
+    }
97
+
98
+    float sy;
99
+    if (height > 1) {
100
+        sy = (float)(original_height - 1) / (float) (height - 1);
101
+    } else {
102
+        sy = (float)(original_height - 1);
103
+    }
104
+
105
+    if (sx < 1.0 && sy < 1.0) { // Magnify both width and height: use weighted sample of 4 pixels
106
+        for (int i = 0; i < height; ++i) {
107
+            int i0 = (int)(i * sy);
108
+            int i1 = i0 + 1;
109
+
110
+            if (i1 >= original_height) {
111
+                i1 = original_height - 1;
112
+            }
113
+
114
+            float alpha = i * sy - i0;
115
+
116
+            for (int j = 0; j < width; ++j) {
117
+                int j0 = (int) (j * sx);
118
+                int j1 = j0 + 1;
119
+
120
+                if (j1 >= original_width) {
121
+                    j1 = original_width - 1;
122
+                }
123
+
124
+                float beta = j * sx - j0;
125
+
126
+                // Compute weighted average of pixels in rect (i0,j0)-(i1,j1)
127
+                float *src00 = tempin + (i0 * original_width + j0) * components;
128
+                float *src01 = tempin + (i0 * original_width + j1) * components;
129
+                float *src10 = tempin + (i1 * original_width + j0) * components;
130
+                float *src11 = tempin + (i1 * original_width + j1) * components;
131
+
132
+                float *dst = tempout + (i * width + j) * components;
133
+
134
+                for (int k = 0; k < components; ++k) {
135
+                    float s1 = *src00++ * (1.0f - beta) + *src01++ * beta;
136
+                    float s2 = *src10++ * (1.0f - beta) + *src11++ * beta;
137
+                    *dst++ = s1 * (1.0f - alpha) + s2 * alpha;
138
+                }
139
+            }
140
+        }
141
+    } else { // Shrink width and/or height: use an unweighted box filter
142
+        for (int i = 0; i < height; ++i) {
143
+            int i0 = (int) (i * sy);
144
+            int i1 = i0 + 1;
145
+
146
+            if (i1 >= original_height) {
147
+                i1 = original_height - 1;
148
+            }
149
+
150
+            for (int j = 0; j < width; ++j) {
151
+                int j0 = (int) (j * sx);
152
+                int j1 = j0 + 1;
153
+
154
+                if (j1 >= original_width) {
155
+                    j1 = original_width - 1;
156
+                }
157
+
158
+                float *dst = tempout + (i * width + j) * components;
159
+
160
+                // Compute average of pixels in the rectangle (i0,j0)-(i1,j1)
161
+                for (int k = 0; k < components; ++k) {
162
+                    float sum = 0.0;
163
+
164
+                    for (int ii = i0; ii <= i1; ++ii) {
165
+                        for (int jj = j0; jj <= j1; ++jj) {
166
+                            sum += *(tempin + (ii * original_width + jj)
167
+                                    * components + k);
168
+                        }
169
+                    }
170
+
171
+                    sum /= (j1 - j0 + 1) * (i1 - i0 + 1);
172
+                    *dst++ = sum;
173
+                }
174
+            }
175
+        }
176
+    }
177
+
178
+    // Copy to our results.
179
+    for (int i = 0; i < height * width * components; ++i) {
180
+        timage[i] = (unsigned char)tempout[i];
181
+    }
182
+
183
+    // Delete our temp buffers.
184
+    delete [] tempin;
185
+    delete [] tempout;
186
+
187
+    return timage;
188
+}
189
+

+ 72
- 28
src/utils/png.cpp Просмотреть файл

@@ -12,6 +12,7 @@
12 12
 #include <cstdio>
13 13
 
14 14
 #include "global.h"
15
+#include "utils/pixel.h"
15 16
 #include "utils/png.h"
16 17
 
17 18
 #ifdef DEBUG
@@ -21,7 +22,30 @@
21 22
 void pngPrint(...) { }
22 23
 #endif
23 24
 
24
-int pngLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height) {
25
+int pngCheck(const char *filename) {
26
+    png_byte header[8];
27
+
28
+    assert(filename != NULL);
29
+    assert(filename[0] != '\0');
30
+
31
+    FILE *fp = fopen(filename, "rb");
32
+    if (fp == NULL) {
33
+        pngPrint("Could not open %s", filename);
34
+        return -1;
35
+    }
36
+
37
+    fread(header, 1, 8, fp);
38
+    fclose(fp);
39
+
40
+    if (png_sig_cmp(header, 0, 8)) {
41
+        pngPrint("File %s is not a PNG.", filename);
42
+        return -2;
43
+    }
44
+
45
+    return 0;
46
+}
47
+
48
+int pngLoad(const char *filename, unsigned char **image, unsigned int *width, unsigned int *height, TextureManager::ColorMode *mode, unsigned int *bpp) {
25 49
     png_byte header[8];
26 50
 
27 51
     assert(filename != NULL);
@@ -29,6 +53,8 @@ int pngLoad(const char *filename, unsigned char **image, unsigned int *width, un
29 53
     assert(image != NULL);
30 54
     assert(width != NULL);
31 55
     assert(height != NULL);
56
+    assert(mode != NULL);
57
+    assert(bpp != NULL);
32 58
 
33 59
     FILE *fp = fopen(filename, "rb");
34 60
     if (fp == NULL) {
@@ -104,34 +130,33 @@ int pngLoad(const char *filename, unsigned char **image, unsigned int *width, un
104 130
     delete [] row_pointers;
105 131
     fclose(fp);
106 132
 
107
-    // Fix alpha
108
-    if (color_type != PNG_COLOR_TYPE_RGB_ALPHA) {
109
-        unsigned char *tmpimage = image_data;
110
-        image_data = new unsigned char[*width * *height * 4];
111
-        if (color_type == PNG_COLOR_TYPE_RGB) {
112
-            for (unsigned int i = 0; i < (*width * *height); i++) {
113
-                image_data[i * 4] = tmpimage[i * 3];
114
-                image_data[(i * 4) + 1] = tmpimage[(i * 3) + 1];
115
-                image_data[(i * 4) + 2] = tmpimage[(i * 3) + 2];
116
-                image_data[(i * 4) + 3] = 255;
117
-            }
118
-        } else {
119
-            pngPrint("%s: Unknown libpng color type %d.", filename, color_type);
120
-            delete [] image_data;
121
-            delete [] tmpimage;
122
-            return -8;
123
-        }
124
-        delete [] tmpimage;
133
+    if (color_type == PNG_COLOR_TYPE_GRAY) {
134
+        *mode = TextureManager::GREYSCALE;
135
+        *bpp = 8;
136
+    } else if (color_type == PNG_COLOR_TYPE_RGB) {
137
+        *mode = TextureManager::RGB;
138
+        *bpp = 24;
139
+    } else if (color_type == PNG_COLOR_TYPE_RGB_ALPHA) {
140
+        *mode = TextureManager::RGBA;
141
+        *bpp = 32;
142
+    } else {
143
+        pngPrint("%s: Unknown libpng color type %d.", filename, color_type);
144
+        delete [] image_data;
145
+        return -8;
125 146
     }
126 147
 
127 148
     // Flip
128
-    *image = new unsigned char[*width * *height * 4];
149
+    *image = new unsigned char[*width * *height * (*bpp / 8)];
129 150
     for (unsigned int y = 0; y < (*height); y++) {
130 151
         for (unsigned int x = 0; x < (*width); x++) {
131
-            (*image)[((y * *width) + x) * 4] = image_data[(((*height - y - 1) * *width) + x) * 4];
132
-            (*image)[(((y * *width) + x) * 4) + 1] = image_data[((((*height - y - 1) * *width) + x) * 4) + 1];
133
-            (*image)[(((y * *width) + x) * 4) + 2] = image_data[((((*height - y - 1) * *width) + x) * 4) + 2];
134
-            (*image)[(((y * *width) + x) * 4) + 3] = image_data[((((*height - y - 1) * *width) + x) * 4) + 3];
152
+            (*image)[((y * *width) + x) * (*bpp / 8)]
153
+                = image_data[(((*height - y - 1) * *width) + x) * (*bpp / 8)];
154
+            (*image)[(((y * *width) + x) * (*bpp / 8)) + 1]
155
+                = image_data[((((*height - y - 1) * *width) + x) * (*bpp / 8)) + 1];
156
+            (*image)[(((y * *width) + x) * (*bpp / 8)) + 2]
157
+                = image_data[((((*height - y - 1) * *width) + x) * (*bpp / 8)) + 2];
158
+            (*image)[(((y * *width) + x) * (*bpp / 8)) + 3]
159
+                = image_data[((((*height - y - 1) * *width) + x) * (*bpp / 8)) + 3];
135 160
         }
136 161
     }
137 162
 
@@ -139,7 +164,7 @@ int pngLoad(const char *filename, unsigned char **image, unsigned int *width, un
139 164
     return 0;
140 165
 }
141 166
 
142
-int pngSave(const char *filename, unsigned char *image, unsigned int width, unsigned int height) {
167
+int pngSave(const char *filename, unsigned char *image, unsigned int width, unsigned int height, TextureManager::ColorMode mode, unsigned int bpp) {
143 168
     assert(filename != NULL);
144 169
     assert(filename[0] != '\0');
145 170
     assert(image != NULL);
@@ -181,8 +206,27 @@ int pngSave(const char *filename, unsigned char *image, unsigned int width, unsi
181 206
         return -5;
182 207
     }
183 208
 
209
+    int color_type;
210
+    if ((mode == TextureManager::GREYSCALE) && (bpp == 8)) {
211
+        color_type = PNG_COLOR_TYPE_GRAY;
212
+    } else if (((mode == TextureManager::RGB) || (mode == TextureManager::BGR)) && (bpp == 24)) {
213
+        if (mode == TextureManager::BGR) {
214
+            bgr2rgb24(image, width, height);
215
+        }
216
+        color_type = PNG_COLOR_TYPE_RGB;
217
+    } else if (((mode == TextureManager::RGBA) || (mode == TextureManager::BGRA)) && (bpp == 32)) {
218
+        if (mode == TextureManager::BGRA) {
219
+            bgra2rgba32(image, width, height);
220
+        }
221
+        color_type = PNG_COLOR_TYPE_RGB_ALPHA;
222
+    } else {
223
+        pngPrint("Error invalid color mode");
224
+        fclose(fp);
225
+        return -6;
226
+    }
227
+
184 228
     png_set_IHDR(png_ptr, info_ptr, width, height,
185
-            8, PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE,
229
+            8, color_type, PNG_INTERLACE_NONE,
186 230
             PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
187 231
 
188 232
     png_write_info(png_ptr, info_ptr);
@@ -196,7 +240,7 @@ int pngSave(const char *filename, unsigned char *image, unsigned int width, unsi
196 240
         pngPrint("Error during writing bytes");
197 241
         delete [] row_pointers;
198 242
         fclose(fp);
199
-        return -6;
243
+        return -7;
200 244
     }
201 245
 
202 246
     png_write_image(png_ptr, row_pointers);
@@ -205,7 +249,7 @@ int pngSave(const char *filename, unsigned char *image, unsigned int width, unsi
205 249
         pngPrint("Error during end of write");
206 250
         delete [] row_pointers;
207 251
         fclose(fp);
208
-        return -7;
252
+        return -8;
209 253
     }
210 254
 
211 255
     png_write_end(png_ptr, NULL);

+ 24
- 25
src/utils/tga.cpp Просмотреть файл

@@ -15,6 +15,30 @@
15 15
 #include "global.h"
16 16
 #include "utils/tga.h"
17 17
 
18
+typedef enum {
19
+    //TGA_TYPE__NO_DATA    = 0,
20
+    //TGA_TYPE__MAPPED     = 1,
21
+    TGA_TYPE__COLOR      = 2,
22
+    TGA_TYPE__GREYSCALE  = 3,
23
+    //TGA_TYPE__MAPPED_RLE = 9,
24
+    TGA_TYPE__COLOR_RLE  = 10
25
+} tga_type_t;
26
+
27
+typedef struct {
28
+    unsigned char comment_lenght;   //!< Number of bytes in comment
29
+    unsigned char colormap_type;    //!< 0 No colormap; 1 Has colormap
30
+    unsigned char image_type;       //!< 1 Colormapped; 2 Unmapped; 9 Colormapped RLE; 10 Unmapped RLE
31
+    unsigned short colormap_index;  //!< Index of first color map entry
32
+    unsigned short colormap_lenght; //!< Number of color map entries
33
+    unsigned char colormap_bbp;     //!< 16, 24, or 32 bits per pixel format
34
+    unsigned short origin_x;        //!< X coor of lower-left corner
35
+    unsigned short origin_y;        //!< Y coor of lower-left corner
36
+    unsigned short width;           //!< Width in pixels
37
+    unsigned short height;          //!< Height in pixels
38
+    unsigned char bpp;              //!< Number of bits in a pixel index
39
+    unsigned char desc_flags;       //!< Various magic bits
40
+} tga_t;
41
+
18 42
 int tgaCheck(FILE *f) {
19 43
     char buffer[10];
20 44
 
@@ -355,28 +379,3 @@ int tgaSave(FILE *f, unsigned char *image, unsigned int width, unsigned int heig
355 379
     return 0;
356 380
 }
357 381
 
358
-int tgaSaveFilename(unsigned char *image, unsigned int width, unsigned int height, char type, const char *s, ...) {
359
-    char buffer[1024];
360
-    FILE *f;
361
-    int v;
362
-    va_list args;
363
-
364
-    assert(image != NULL);
365
-    assert(width > 0);
366
-    assert(height > 0);
367
-    assert(s != NULL);
368
-    assert(s[0] != '\0');
369
-
370
-    va_start(args, s);
371
-    vsnprintf(buffer, 1023, s, args);
372
-    va_end(args);
373
-    f = fopen(buffer, "wb");
374
-    if (!f) {
375
-        perror(buffer);
376
-        return -1;
377
-    }
378
-    v = tgaSave(f, image, width, height, type);
379
-    fclose(f);
380
-    return v;
381
-}
382
-

Загрузка…
Отмена
Сохранить