1 /***************************************************************************
2  *   Free Heroes of Might and Magic II: https://github.com/ihhub/fheroes2  *
3  *   Copyright (C) 2020                                                    *
4  *                                                                         *
5  *   This program is free software; you can redistribute it and/or modify  *
6  *   it under the terms of the GNU General Public License as published by  *
7  *   the Free Software Foundation; either version 2 of the License, or     *
8  *   (at your option) any later version.                                   *
9  *                                                                         *
10  *   This program is distributed in the hope that it will be useful,       *
11  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
12  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
13  *   GNU General Public License for more details.                          *
14  *                                                                         *
15  *   You should have received a copy of the GNU General Public License     *
16  *   along with this program; if not, write to the                         *
17  *   Free Software Foundation, Inc.,                                       *
18  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
19  ***************************************************************************/
20 
21 #include "image.h"
22 #include "image_palette.h"
23 
24 #include <cassert>
25 #include <cstdlib>
26 #include <cstring>
27 
28 namespace
29 {
30     // 0 in shadow part means no shadow, 1 means skip any drawings so to don't waste extra CPU cycles for ( tableId - 2 ) command we just add extra fake tables
31     // Mirror palette was modified as it was containing 238, 238, 239, 240 values instead of 238, 239, 240, 241
32     const uint8_t transformTable[256 * 16] = {
33         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
34         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
35         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
36         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
37         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
38         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
39         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
40         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
41         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
42 
43         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
44         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
45         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
46         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
47         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
48         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
49         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
50         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
51         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   0,
52 
53         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,  33,  34,
54         35,  36,  36,  36,  36,  36,  36,  36,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,  62,  62,
55         62,  62,  62,  62,  62,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  84,  84,  84,  84,  84,  91,  92,
56         93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 107, 107, 107, 107, 107, 107, 114, 115, 116, 117, 118, 119, 120, 121,
57         122, 123, 124, 125, 126, 127, 128, 129, 130, 130, 130, 130, 130, 130, 130, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149,
58         150, 151, 151, 151, 151, 151, 151, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 174, 174, 174, 174, 174,
59         174, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 197, 197, 197, 197, 197, 202, 203, 204, 205, 206,
60         207, 208, 209, 210, 211, 212, 213, 213, 213, 213, 213, 214, 215, 216, 217, 218, 219, 220, 221, 225, 226, 227, 228, 229, 230, 230, 230, 230, 73,
61         75,  77,  79,  81,  76,  78,  74,  76,  78,  80,  244, 245, 245, 245, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // First
62 
63         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,  31,  32,
64         33,  34,  35,  36,  36,  36,  36,  36,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,  60,  61,
65         62,  62,  62,  62,  62,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  84,  84,  84,  89,  90,
66         91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 107, 107, 107, 107, 112, 113, 114, 115, 116, 117, 118, 119,
67         120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 130, 130, 130, 130, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147,
68         148, 149, 150, 151, 151, 151, 151, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 174, 174, 174,
69         174, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 197, 197, 197, 201, 202, 203, 204, 205,
70         206, 207, 208, 209, 210, 211, 212, 213, 213, 213, 213, 214, 215, 216, 217, 218, 219, 220, 221, 224, 225, 226, 227, 228, 229, 230, 230, 230, 76,
71         76,  76,  76,  76,  76,  76,  76,  76,  76,  78,  244, 245, 245, 245, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // Second
72 
73         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,  30,
74         31,  32,  33,  34,  35,  36,  36,  36,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,  59,
75         60,  61,  62,  62,  62,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  84,  84,  87,  88,
76         89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 107, 107, 110, 111, 112, 113, 114, 115, 116, 117,
77         118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 130, 130, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146,
78         147, 148, 149, 150, 151, 151, 151, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 174,
79         174, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 197, 197, 200, 201, 202, 203, 204,
80         205, 206, 207, 208, 209, 210, 211, 212, 213, 213, 213, 214, 215, 216, 217, 218, 219, 220, 221, 223, 224, 225, 226, 227, 228, 229, 230, 230, 76,
81         76,  76,  76,  76,  76,  76,  76,  76,  76,  76,  243, 244, 245, 245, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // Third
82 
83         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,  29,
84         30,  31,  32,  33,  34,  35,  36,  36,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,  58,
85         59,  60,  61,  62,  62,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  84,  86,  87,
86         88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 107, 109, 110, 111, 112, 113, 114, 115, 116,
87         117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 130, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145,
88         146, 147, 148, 149, 150, 151, 151, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174,
89         174, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 197, 199, 200, 201, 202, 203,
90         204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 213, 214, 215, 216, 217, 218, 219, 220, 221, 223, 224, 225, 226, 227, 228, 229, 230, 230, 75,
91         75,  75,  75,  75,  75,  75,  75,  75,  75,  75,  243, 244, 245, 245, 0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // Fourth
92 
93         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   10,  10,  11,  11,  11,  12,  13,  13,  13,  14,  14,  15,  15,  15,  16,  17,  17,  17,  18,
94         18,  19,  19,  20,  20,  20,  21,  21,  11,  37,  37,  37,  38,  38,  39,  39,  39,  40,  40,  41,  41,  41,  41,  42,  42,  19,  42,  20,  20,
95         20,  20,  20,  20,  21,  12,  131, 63,  63,  63,  64,  64,  64,  65,  65,  65,  65,  65,  242, 242, 242, 242, 242, 242, 242, 242, 242, 13,  14,
96         15,  15,  16,  85,  17,  85,  85,  85,  85,  19,  86,  20,  20,  20,  21,  21,  21,  21,  21,  21,  21,  10,  108, 108, 109, 109, 109, 110, 110,
97         110, 110, 199, 40,  41,  41,  41,  41,  41,  42,  42,  42,  42,  20,  20,  11,  11,  131, 131, 132, 132, 132, 133, 133, 134, 134, 134, 135, 135,
98         18,  136, 19,  19,  20,  20,  20,  10,  11,  11,  11,  12,  12,  13,  13,  13,  14,  15,  15,  15,  16,  17,  17,  17,  18,  18,  19,  19,  20,
99         20,  11,  175, 175, 176, 176, 38,  177, 177, 178, 178, 178, 179, 179, 179, 179, 180, 180, 180, 180, 180, 180, 21,  21,  108, 108, 38,  109, 38,
100         109, 39,  40,  40,  41,  41,  41,  42,  42,  42,  20,  199, 179, 180, 180, 110, 110, 40,  42,  110, 110, 86,  86,  86,  86,  18,  18,  19,  65,
101         65,  65,  66,  65,  66,  65,  152, 155, 65,  242, 15,  16,  17,  19,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // Fifth
102 
103         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   10,  11,  11,  12,  12,  13,  13,  14,  15,  15,  16,  16,  17,  17,  18,  19,  20,  20,  21,
104         21,  22,  22,  23,  24,  24,  25,  25,  37,  37,  38,  38,  39,  39,  40,  41,  41,  41,  42,  42,  43,  43,  44,  44,  45,  45,  46,  46,  23,
105         24,  24,  24,  24,  24,  131, 63,  63,  64,  64,  65,  65,  66,  66,  242, 67,  67,  68,  68,  243, 243, 243, 243, 243, 243, 243, 243, 15,  15,
106         85,  85,  85,  85,  86,  86,  87,  87,  88,  88,  88,  88,  89,  24,  90,  25,  25,  25,  25,  25,  25,  37,  108, 109, 109, 110, 110, 111, 111,
107         200, 200, 201, 201, 42,  43,  43,  44,  44,  44,  45,  45,  46,  46,  46,  11,  131, 132, 132, 132, 133, 133, 134, 135, 135, 136, 242, 137, 137,
108         138, 243, 243, 243, 243, 243, 24,  152, 152, 153, 153, 154, 154, 155, 156, 156, 157, 158, 158, 159, 18,  19,  19,  20,  20,  21,  22,  22,  23,
109         24,  37,  175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 180, 181, 181, 181, 182, 182, 182, 46,  47,  47,  48,  25,  108, 109, 109, 109, 198,
110         199, 199, 201, 201, 42,  43,  43,  44,  45,  46,  46,  201, 181, 182, 183, 111, 111, 202, 45,  111, 111, 87,  88,  88,  88,  88,  21,  22,  66,
111         66,  68,  68,  67,  68,  68,  152, 157, 66,  69,  16,  18,  20,  21,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // Sixth
112 
113         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   10,  11,  11,  12,  13,  14,  14,  15,  16,  17,  17,  18,  19,  20,  20,  21,  22,  23,  24,
114         24,  25,  26,  26,  27,  28,  29,  29,  37,  37,  38,  39,  40,  40,  41,  42,  42,  43,  44,  44,  45,  46,  46,  47,  47,  48,  48,  49,  50,
115         50,  27,  28,  28,  28,  63,  63,  64,  65,  65,  66,  67,  67,  68,  69,  69,  69,  70,  70,  70,  244, 71,  244, 244, 244, 244, 245, 16,  85,
116         85,  86,  87,  87,  88,  88,  89,  90,  90,  91,  91,  91,  92,  93,  93,  93,  29,  29,  29,  29,  29,  37,  109, 109, 110, 111, 111, 112, 113,
117         112, 112, 203, 203, 203, 44,  45,  46,  47,  47,  47,  48,  48,  49,  50,  131, 131, 132, 133, 133, 134, 135, 136, 136, 137, 137, 139, 139, 139,
118         141, 141, 141, 143, 143, 245, 245, 152, 152, 153, 154, 155, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163, 163, 164, 165, 165, 166, 26,  26,
119         27,  175, 13,  176, 177, 178, 178, 179, 180, 181, 181, 182, 182, 183, 183, 183, 184, 184, 185, 185, 50,  50,  52,  52,  109, 109, 198, 199, 200,
120         201, 201, 202, 202, 44,  45,  46,  47,  48,  48,  49,  204, 205, 185, 185, 112, 112, 204, 47,  112, 113, 88,  89,  91,  92,  93,  93,  25,  66,
121         68,  69,  69,  68,  69,  69,  153, 159, 68,  71,  18,  242, 243, 24,  0,   0,   0,   0,   0,   0,   0,   0,   0,   0, // Seventh
122 
123         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   10,  11,  12,  13,  13,  14,  15,  16,  17,  17,  19,  19,  20,  21,  22,  23,  24,  24,  26,
124         26,  27,  28,  28,  30,  30,  31,  32,  37,  38,  39,  39,  40,  41,  42,  43,  43,  44,  45,  46,  46,  47,  48,  49,  50,  50,  51,  52,  52,
125         53,  54,  54,  30,  31,  63,  64,  64,  65,  66,  67,  68,  69,  69,  70,  71,  71,  71,  72,  72,  72,  73,  73,  73,  168, 168, 168, 85,  85,
126         86,  87,  88,  88,  89,  90,  91,  91,  92,  93,  93,  94,  95,  95,  96,  96,  96,  31,  32,  32,  32,  108, 109, 198, 110, 111, 112, 113, 113,
127         113, 116, 117, 118, 119, 120, 121, 47,  48,  50,  50,  51,  51,  52,  52,  131, 132, 132, 133, 134, 135, 136, 137, 137, 138, 139, 140, 141, 141,
128         143, 143, 144, 145, 146, 147, 30,  152, 153, 153, 154, 155, 156, 157, 158, 158, 159, 160, 161, 162, 163, 164, 165, 165, 166, 167, 168, 169, 28,
129         29,  175, 176, 177, 177, 178, 179, 180, 181, 182, 182, 183, 184, 185, 185, 185, 186, 186, 187, 50,  52,  52,  54,  55,  109, 198, 199, 200, 201,
130         202, 202, 204, 204, 205, 207, 47,  49,  50,  51,  52,  206, 206, 187, 188, 113, 113, 118, 49,  222, 222, 223, 224, 225, 226, 95,  227, 228, 67,
131         68,  70,  71,  69,  71,  70,  153, 65,  69,  73,  242, 22,  243, 244, 0,   0,   0,   0,   0,   0,   0,   0,   0,
132         0, // Eighth
133 
134         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   10,  11,  11,  12,  12,  13,  14,  14,  15,  16,  16,  17,  18,  18,  19,  20,  242, 242, 22,
135         22,  23,  243, 243, 25,  244, 244, 244, 11,  37,  38,  38,  39,  40,  40,  41,  178, 18,  19,  20,  20,  21,  21,  22,  22,  22,  22,  22,  23,
136         23,  23,  23,  24,  244, 63,  63,  64,  64,  65,  65,  66,  66,  67,  67,  68,  68,  68,  69,  69,  69,  69,  69,  69,  70,  70,  70,  15,  15,
137         16,  85,  86,  18,  19,  19,  20,  159, 21,  21,  161, 22,  163, 163, 163, 23,  23,  165, 165, 244, 244, 37,  108, 38,  109, 109, 110, 199, 200,
138         199, 40,  41,  42,  42,  42,  43,  43,  44,  22,  22,  23,  23,  23,  23,  131, 131, 132, 132, 133, 133, 134, 134, 135, 135, 136, 136, 137, 137,
139         138, 138, 139, 139, 140, 141, 244, 152, 152, 153, 153, 154, 154, 155, 155, 156, 156, 157, 158, 158, 159, 242, 159, 161, 161, 243, 243, 243, 243,
140         164, 11,  175, 176, 176, 177, 177, 178, 179, 179, 180, 180, 181, 181, 182, 182, 182, 182, 183, 22,  23,  23,  23,  23,  108, 38,  38,  39,  39,
141         40,  40,  41,  178, 180, 42,  44,  45,  23,  23,  23,  180, 181, 181, 183, 110, 200, 42,  45,  85,  86,  87,  87,  87,  21,  22,  22,  23,  66,
142         66,  67,  68,  67,  68,  68,  153, 158, 67,  70,  64,  65,  242, 243, 159, 159, 159, 159, 159, 159, 159, 159, 159, 10, // Nineth
143 
144         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   10,  11,  11,  12,  13,  14,  14,  15,  16,  16,  17,  18,  19,  19,  20,  242, 22,  22,  243,
145         243, 243, 244, 244, 244, 244, 245, 245, 37,  37,  38,  176, 39,  177, 41,  41,  42,  179, 20,  180, 45,  22,  23,  23,  24,  24,  24,  24,  25,
146         25,  25,  25,  26,  26,  63,  63,  64,  64,  65,  66,  67,  67,  68,  68,  69,  69,  70,  70,  70,  70,  70,  71,  71,  71,  71,  71,  15,  85,
147         85,  86,  86,  87,  20,  88,  89,  22,  161, 162, 163, 163, 164, 164, 165, 165, 166, 166, 167, 167, 167, 37,  108, 109, 109, 110, 199, 111, 111,
148         200, 201, 41,  43,  43,  43,  44,  44,  46,  46,  24,  24,  25,  25,  25,  131, 131, 132, 132, 133, 134, 135, 135, 136, 136, 137, 138, 138, 139,
149         139, 140, 140, 141, 141, 142, 143, 152, 152, 153, 153, 154, 155, 155, 156, 156, 157, 158, 158, 159, 159, 161, 161, 162, 162, 163, 164, 244, 244,
150         244, 175, 175, 176, 177, 177, 178, 179, 179, 180, 181, 181, 182, 182, 183, 183, 183, 184, 184, 184, 25,  25,  25,  25,  108, 109, 109, 39,  40,
151         41,  41,  42,  42,  43,  43,  45,  46,  47,  25,  25,  181, 182, 183, 185, 111, 111, 42,  46,  111, 87,  88,  88,  88,  22,  23,  24,  25,  66,
152         67,  68,  69,  68,  69,  69,  153, 0,   68,  71,  65,  242, 242, 243, 0,   0,   0,   0,   0,   0,   0,   0,   0,   10, // Tenth
153 
154         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   10,  11,  11,  13,  13,  14,  15,  16,  16,  17,  18,  19,  20,  21,  21,  22,  23,  243, 25,
155         244, 244, 244, 28,  245, 245, 245, 31,  37,  38,  38,  39,  40,  41,  41,  42,  42,  180, 45,  46,  46,  47,  47,  25,  26,  26,  26,  26,  27,
156         27,  27,  27,  27,  245, 63,  63,  64,  65,  66,  66,  67,  68,  69,  69,  70,  70,  71,  71,  72,  72,  72,  72,  72,  73,  73,  73,  16,  85,
157         85,  86,  87,  88,  88,  90,  90,  91,  91,  163, 164, 164, 165, 166, 166, 167, 167, 168, 169, 169, 170, 37,  108, 109, 198, 199, 111, 112, 112,
158         201, 202, 202, 43,  44,  45,  45,  46,  46,  47,  48,  26,  27,  27,  27,  131, 131, 132, 133, 134, 135, 135, 136, 137, 137, 138, 139, 139, 140,
159         141, 141, 142, 143, 143, 144, 145, 152, 152, 153, 154, 155, 155, 156, 156, 158, 158, 159, 160, 160, 161, 162, 163, 164, 164, 165, 166, 166, 167,
160         167, 175, 176, 176, 177, 178, 178, 179, 180, 181, 182, 182, 183, 184, 184, 184, 185, 186, 186, 50,  51,  27,  27,  27,  109, 109, 109, 40,  40,
161         41,  42,  43,  43,  44,  45,  46,  47,  49,  27,  27,  182, 183, 184, 187, 112, 112, 43,  47,  112, 87,  89,  90,  91,  91,  24,  26,  26,  67,
162         68,  69,  70,  69,  70,  70,  153, 0,   0,   73,  65,  242, 243, 244, 0,   0,   0,   0,   0,   0,   0,   0,   0,
163         10, // Eleventh
164 
165         0,   0,   0,   0,   0,   0,   0,   0,   0,   0,   10,  11,  12,  13,  13,  14,  15,  16,  17,  18,  19,  20,  21,  21,  23,  243, 24,  25,  244,
166         244, 27,  245, 245, 31,  170, 149, 149, 37,  38,  38,  39,  40,  41,  42,  42,  44,  45,  46,  46,  47,  48,  49,  50,  51,  28,  28,  28,  29,
167         29,  29,  29,  29,  30,  63,  64,  65,  65,  66,  67,  68,  69,  70,  70,  71,  72,  72,  73,  73,  74,  74,  75,  75,  76,  76,  76,  85,  85,
168         86,  87,  88,  89,  90,  91,  91,  92,  93,  93,  166, 166, 96,  168, 168, 169, 170, 170, 171, 171, 171, 37,  109, 109, 110, 200, 111, 112, 113,
169         202, 202, 203, 44,  45,  46,  46,  47,  48,  49,  50,  51,  52,  29,  29,  131, 132, 133, 133, 134, 135, 136, 137, 138, 139, 139, 140, 141, 142,
170         142, 144, 144, 145, 145, 146, 147, 152, 153, 153, 154, 155, 156, 157, 158, 159, 159, 160, 161, 162, 163, 164, 164, 165, 166, 167, 168, 168, 168,
171         169, 175, 176, 177, 177, 178, 179, 180, 181, 182, 182, 183, 184, 185, 186, 186, 187, 187, 189, 189, 193, 193, 146, 146, 109, 109, 198, 199, 201,
172         201, 201, 44,  205, 45,  46,  47,  48,  50,  52,  29,  183, 185, 186, 189, 112, 112, 205, 49,  222, 88,  89,  91,  92,  93,  26,  27,  28,  67,
173         68,  70,  71,  69,  71,  71,  154, 0,   0,   75,  242, 242, 243, 244, 0,   0,   0,   0,   0,   0,   0,   0,   0,   10, // Twelfth
174 
175         0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  10,  10,  10,  10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,
176         25,  26,  27,  28,  29,  30,  31,  32,  37,  37,  37,  37,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,
177         54,  55,  56,  57,  58,  63,  63,  63,  63,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  85,  85,
178         85,  85,  85,  86,  87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 108, 108, 108, 108, 108, 109, 110, 111,
179         112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 131, 131, 131, 131, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140,
180         141, 142, 143, 144, 145, 146, 147, 152, 152, 152, 152, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169,
181         170, 175, 175, 175, 175, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 198, 198, 198, 198, 198,
182         199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 214, 215, 216, 217, 218, 219, 220, 221, 222, 222, 223, 224, 225, 226, 227, 228, 229, 231,
183         232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 242, 243, 244, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, // Mirror
184 
185         0,   1,   2,   3,   4,   5,   6,   7,   8,   9,   10,  11,  12,  13,  14,  15,  16,  17,  18,  19,  20,  21,  22,  23,  24,  25,  26,  27,  28,
186         29,  30,  31,  32,  33,  34,  35,  36,  37,  38,  39,  40,  41,  42,  43,  44,  45,  46,  47,  48,  49,  50,  51,  52,  53,  54,  55,  56,  57,
187         58,  59,  60,  61,  62,  63,  64,  65,  66,  67,  68,  69,  70,  71,  72,  73,  74,  75,  76,  77,  78,  79,  80,  81,  82,  83,  84,  85,  86,
188         87,  88,  89,  90,  91,  92,  93,  94,  95,  96,  97,  98,  99,  100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115,
189         116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144,
190         145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173,
191         174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202,
192         203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 188, 188, 188, 188, 118, 118, 118, 118, 222, 223, 224, 225, 226, 227, 228, 229, 230, 69,
193         69,  69,  69,  69,  69,  69,  69,  69,  69,  69,  242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 // No cycle
194     };
195 
Validate(const fheroes2::Image & image,int32_t x,int32_t y,int32_t width,int32_t height)196     bool Validate( const fheroes2::Image & image, int32_t x, int32_t y, int32_t width, int32_t height )
197     {
198         if ( image.empty() || width <= 0 || height <= 0 ) // what's the reason to work with empty images?
199             return false;
200 
201         if ( x < 0 || y < 0 || x + width > image.width() || y + height > image.height() )
202             return false;
203 
204         return true;
205     }
206 
Verify(const fheroes2::Image & image,int32_t & x,int32_t & y,int32_t & width,int32_t & height)207     bool Verify( const fheroes2::Image & image, int32_t & x, int32_t & y, int32_t & width, int32_t & height )
208     {
209         if ( image.empty() || width <= 0 || height <= 0 ) // what's the reason to work with empty images?
210             return false;
211 
212         const int32_t widthOut = image.width();
213         const int32_t heightOut = image.height();
214 
215         if ( x < 0 ) {
216             const int32_t offsetX = -x;
217             if ( offsetX >= width )
218                 return false;
219 
220             x = 0;
221             width -= offsetX;
222         }
223 
224         if ( y < 0 ) {
225             const int32_t offsetY = -y;
226             if ( offsetY >= height )
227                 return false;
228 
229             y = 0;
230             height -= offsetY;
231         }
232 
233         if ( x > widthOut || y > heightOut )
234             return false;
235 
236         if ( x + width > widthOut ) {
237             const int32_t offsetX = x + width - widthOut;
238             if ( offsetX >= width )
239                 return false;
240             width -= offsetX;
241         }
242 
243         if ( y + height > heightOut ) {
244             const int32_t offsetY = y + height - heightOut;
245             if ( offsetY >= height )
246                 return false;
247             height -= offsetY;
248         }
249 
250         return true;
251     }
252 
Verify(int32_t & inX,int32_t & inY,int32_t & outX,int32_t & outY,int32_t & width,int32_t & height,int32_t widthIn,int32_t heightIn,int32_t widthOut,int32_t heightOut)253     bool Verify( int32_t & inX, int32_t & inY, int32_t & outX, int32_t & outY, int32_t & width, int32_t & height, int32_t widthIn, int32_t heightIn, int32_t widthOut,
254                  int32_t heightOut )
255     {
256         if ( widthIn <= 0 || heightIn <= 0 || widthOut <= 0 || heightOut <= 0 || width <= 0 || height <= 0 ) // what's the reason to work with empty images?
257             return false;
258 
259         if ( inX < 0 || inY < 0 || inX > widthIn || inY > heightIn )
260             return false;
261 
262         if ( outX < 0 ) {
263             const int32_t offsetX = -outX;
264             if ( offsetX >= width )
265                 return false;
266 
267             inX += offsetX;
268             outX = 0;
269             width -= offsetX;
270         }
271 
272         if ( outY < 0 ) {
273             const int32_t offsetY = -outY;
274             if ( offsetY >= height )
275                 return false;
276 
277             inY += offsetY;
278             outY = 0;
279             height -= offsetY;
280         }
281 
282         if ( outX > widthOut || outY > heightOut )
283             return false;
284 
285         if ( inX + width > widthIn ) {
286             const int32_t offsetX = inX + width - widthIn;
287             if ( offsetX >= width )
288                 return false;
289             width -= offsetX;
290         }
291 
292         if ( inY + height > heightIn ) {
293             const int32_t offsetY = inY + height - heightIn;
294             if ( offsetY >= height )
295                 return false;
296             height -= offsetY;
297         }
298 
299         if ( outX + width > widthOut ) {
300             const int32_t offsetX = outX + width - widthOut;
301             if ( offsetX >= width )
302                 return false;
303             width -= offsetX;
304         }
305 
306         if ( outY + height > heightOut ) {
307             const int32_t offsetY = outY + height - heightOut;
308             if ( offsetY >= height )
309                 return false;
310             height -= offsetY;
311         }
312 
313         return true;
314     }
315 
Verify(const fheroes2::Image & in,int32_t & inX,int32_t & inY,const fheroes2::Image & out,int32_t & outX,int32_t & outY,int32_t & width,int32_t & height)316     bool Verify( const fheroes2::Image & in, int32_t & inX, int32_t & inY, const fheroes2::Image & out, int32_t & outX, int32_t & outY, int32_t & width,
317                  int32_t & height )
318     {
319         return Verify( inX, inY, outX, outY, width, height, in.width(), in.height(), out.width(), out.height() );
320     }
321 
GetPALColorId(uint8_t red,uint8_t green,uint8_t blue)322     uint8_t GetPALColorId( uint8_t red, uint8_t green, uint8_t blue )
323     {
324         static uint8_t rgbToId[64 * 64 * 64];
325         static bool isInitialized = false;
326         if ( !isInitialized ) {
327             isInitialized = true;
328             const uint32_t size = 64 * 64 * 64;
329 
330             uint32_t r = 0;
331             uint32_t g = 0;
332             uint32_t b = 0;
333 
334             const uint8_t * gamePalette = fheroes2::getGamePalette();
335 
336             for ( uint32_t id = 0; id < size; ++id ) {
337                 r = ( id % 64 );
338                 g = ( id >> 6 ) % 64;
339                 b = ( id >> 12 );
340                 int32_t minDistance = 3 * 255 * 255;
341                 uint32_t bestPos = 0;
342 
343                 const uint8_t * correctorX = transformTable + 256 * 15;
344 
345                 for ( uint32_t i = 0; i < 256; ++i, ++correctorX ) {
346                     const uint8_t * palette = gamePalette + *correctorX * 3;
347 
348                     const int32_t offsetRed = static_cast<int32_t>( *palette ) - static_cast<int32_t>( r );
349                     ++palette;
350                     const int32_t offsetGreen = static_cast<int32_t>( *palette ) - static_cast<int32_t>( g );
351                     ++palette;
352                     const int32_t offsetBlue = static_cast<int32_t>( *palette ) - static_cast<int32_t>( b );
353                     ++palette;
354                     const int32_t distance = offsetRed * offsetRed + offsetGreen * offsetGreen + offsetBlue * offsetBlue;
355                     if ( minDistance > distance ) {
356                         minDistance = distance;
357                         bestPos = *correctorX;
358                     }
359                 }
360 
361                 rgbToId[id] = static_cast<uint8_t>( bestPos ); // it's safe to cast
362             }
363         }
364 
365         return rgbToId[red + green * 64 + blue * 64 * 64];
366     }
367 
ApplyRawPalette(const fheroes2::Image & in,int32_t inX,int32_t inY,fheroes2::Image & out,int32_t outX,int32_t outY,int32_t width,int32_t height,const uint8_t * palette)368     void ApplyRawPalette( const fheroes2::Image & in, int32_t inX, int32_t inY, fheroes2::Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height,
369                           const uint8_t * palette )
370     {
371         if ( !Verify( in, inX, inY, out, outX, outY, width, height ) ) {
372             return;
373         }
374 
375         const int32_t widthIn = in.width();
376         const int32_t widthOut = out.width();
377 
378         const uint8_t * imageInY = in.image() + inY * widthIn + inX;
379         const uint8_t * transformInY = in.transform() + inY * widthIn + inX;
380         uint8_t * imageOutY = out.image() + outY * widthOut + outX;
381         const uint8_t * imageInYEnd = imageInY + height * widthIn;
382 
383         for ( ; imageInY != imageInYEnd; imageInY += widthIn, transformInY += widthIn, imageOutY += widthOut ) {
384             const uint8_t * imageInX = imageInY;
385             const uint8_t * transformInX = transformInY;
386             uint8_t * imageOutX = imageOutY;
387             const uint8_t * imageInXEnd = imageInX + width;
388 
389             for ( ; imageInX != imageInXEnd; ++imageInX, ++imageOutX, ++transformInX ) {
390                 if ( *transformInX == 0 ) { // only modify pixels with data
391                     *imageOutX = palette[*imageInX];
392                 }
393             }
394         }
395     }
396 }
397 
398 namespace fheroes2
399 {
Image()400     Image::Image()
401         : _width( 0 )
402         , _height( 0 )
403         , _singleLayer( false )
404     {}
405 
Image(int32_t width_,int32_t height_)406     Image::Image( int32_t width_, int32_t height_ )
407         : _width( 0 )
408         , _height( 0 )
409         , _singleLayer( false )
410     {
411         resize( width_, height_ );
412     }
413 
Image(const Image & image_)414     Image::Image( const Image & image_ )
415         : _width( 0 )
416         , _height( 0 )
417         , _singleLayer( false )
418     {
419         copy( image_ );
420     }
421 
Image(Image && image_)422     Image::Image( Image && image_ ) noexcept
423         : _width( 0 )
424         , _height( 0 )
425         , _data( std::move( image_._data ) )
426         , _singleLayer( false )
427     {
428         std::swap( _singleLayer, image_._singleLayer );
429         std::swap( _width, image_._width );
430         std::swap( _height, image_._height );
431     }
432 
operator =(const Image & image_)433     Image & Image::operator=( const Image & image_ )
434     {
435         if ( this != &image_ ) {
436             copy( image_ );
437         }
438 
439         return *this;
440     }
441 
operator =(Image && image_)442     Image & Image::operator=( Image && image_ ) noexcept
443     {
444         if ( this != &image_ ) {
445             // We shouldn't copy or move different types of images.
446             assert( _singleLayer == image_._singleLayer );
447 
448             std::swap( _width, image_._width );
449             std::swap( _height, image_._height );
450             std::swap( _data, image_._data );
451         }
452 
453         return *this;
454     }
455 
image()456     uint8_t * Image::image()
457     {
458         return _data.get();
459     }
460 
image() const461     const uint8_t * Image::image() const
462     {
463         return _data.get();
464     }
465 
transform()466     uint8_t * Image::transform()
467     {
468         return _data.get() + _width * _height;
469     }
470 
transform() const471     const uint8_t * Image::transform() const
472     {
473         return _data.get() + _width * _height;
474     }
475 
clear()476     void Image::clear()
477     {
478         _data.reset();
479 
480         _width = 0;
481         _height = 0;
482     }
483 
fill(uint8_t value)484     void Image::fill( uint8_t value )
485     {
486         if ( !empty() ) {
487             const size_t totalSize = static_cast<size_t>( _width * _height );
488             std::fill( image(), image() + totalSize, value );
489             std::fill( transform(), transform() + totalSize, 0 );
490         }
491     }
492 
resize(int32_t width_,int32_t height_)493     void Image::resize( int32_t width_, int32_t height_ )
494     {
495         if ( width_ == _width && height_ == _height ) {
496             return;
497         }
498 
499         if ( width_ <= 0 || height_ <= 0 ) {
500             clear();
501 
502             return;
503         }
504 
505         const size_t size = static_cast<size_t>( width_ * height_ );
506 
507         _data.reset( new uint8_t[size * 2] );
508 
509         _width = width_;
510         _height = height_;
511     }
512 
reset()513     void Image::reset()
514     {
515         if ( !empty() ) {
516             const size_t totalSize = static_cast<size_t>( _width * _height );
517             std::fill( image(), image() + totalSize, 0 );
518             std::fill( transform(), transform() + totalSize, 1 ); // skip all data
519         }
520     }
521 
copy(const Image & image)522     void Image::copy( const Image & image )
523     {
524         // We shouldn't copy or move different types of images.
525         assert( _singleLayer == image._singleLayer );
526 
527         if ( !image._data ) {
528             clear();
529 
530             return;
531         }
532 
533         const size_t size = static_cast<size_t>( image._width * image._height );
534 
535         if ( image._width != _width || image._height != _height ) {
536             _data.reset( new uint8_t[size * 2] );
537 
538             _width = image._width;
539             _height = image._height;
540         }
541 
542         memcpy( _data.get(), image._data.get(), size * 2 );
543     }
544 
Sprite()545     Sprite::Sprite()
546         : Image( 0, 0 )
547         , _x( 0 )
548         , _y( 0 )
549     {}
550 
Sprite(int32_t width_,int32_t height_,int32_t x_,int32_t y_)551     Sprite::Sprite( int32_t width_, int32_t height_, int32_t x_, int32_t y_ )
552         : Image( width_, height_ )
553         , _x( x_ )
554         , _y( y_ )
555     {}
556 
Sprite(const Image & image,int32_t x_,int32_t y_)557     Sprite::Sprite( const Image & image, int32_t x_, int32_t y_ )
558         : Image( image )
559         , _x( x_ )
560         , _y( y_ )
561     {}
562 
Sprite(const Sprite & sprite)563     Sprite::Sprite( const Sprite & sprite )
564         : Image( sprite )
565         , _x( sprite._x )
566         , _y( sprite._y )
567     {}
568 
Sprite(Sprite && sprite)569     Sprite::Sprite( Sprite && sprite ) noexcept
570         : Image( std::move( sprite ) )
571         , _x( 0 )
572         , _y( 0 )
573     {
574         std::swap( _x, sprite._x );
575         std::swap( _y, sprite._y );
576     }
577 
operator =(const Sprite & sprite)578     Sprite & Sprite::operator=( const Sprite & sprite )
579     {
580         if ( this != &sprite ) {
581             Image::operator=( sprite );
582 
583             _x = sprite._x;
584             _y = sprite._y;
585         }
586 
587         return *this;
588     }
589 
operator =(Sprite && sprite)590     Sprite & Sprite::operator=( Sprite && sprite ) noexcept
591     {
592         if ( this != &sprite ) {
593             Image::operator=( std::move( sprite ) );
594 
595             std::swap( _x, sprite._x );
596             std::swap( _y, sprite._y );
597         }
598 
599         return *this;
600     }
601 
setPosition(int32_t x_,int32_t y_)602     void Sprite::setPosition( int32_t x_, int32_t y_ )
603     {
604         _x = x_;
605         _y = y_;
606     }
607 
ImageRestorer(Image & image)608     ImageRestorer::ImageRestorer( Image & image )
609         : _image( image )
610         , _x( 0 )
611         , _y( 0 )
612         , _width( image.width() )
613         , _height( image.height() )
614         , _isRestored( false )
615     {
616         _updateRoi();
617         _copy.resize( _width, _height );
618         if ( _image.singleLayer() ) {
619             _copy._disableTransformLayer();
620         }
621 
622         Copy( _image, _x, _y, _copy, 0, 0, _width, _height );
623     }
624 
ImageRestorer(Image & image,int32_t x_,int32_t y_,int32_t width,int32_t height)625     ImageRestorer::ImageRestorer( Image & image, int32_t x_, int32_t y_, int32_t width, int32_t height )
626         : _image( image )
627         , _x( x_ )
628         , _y( y_ )
629         , _width( width )
630         , _height( height )
631         , _isRestored( false )
632     {
633         _updateRoi();
634         _copy.resize( _width, _height );
635         if ( _image.singleLayer() ) {
636             _copy._disableTransformLayer();
637         }
638 
639         Copy( _image, _x, _y, _copy, 0, 0, _width, _height );
640     }
641 
~ImageRestorer()642     ImageRestorer::~ImageRestorer()
643     {
644         if ( !_isRestored ) {
645             restore();
646         }
647     }
648 
update(int32_t x_,int32_t y_,int32_t width,int32_t height)649     void ImageRestorer::update( int32_t x_, int32_t y_, int32_t width, int32_t height )
650     {
651         _isRestored = false;
652         _x = x_;
653         _y = y_;
654         _width = width;
655         _height = height;
656         _updateRoi();
657 
658         _copy.resize( _width, _height );
659         Copy( _image, _x, _y, _copy, 0, 0, _width, _height );
660     }
661 
restore()662     void ImageRestorer::restore()
663     {
664         _isRestored = true;
665         Copy( _copy, 0, 0, _image, _x, _y, _width, _height );
666     }
667 
reset()668     void ImageRestorer::reset()
669     {
670         _isRestored = true;
671     }
672 
_updateRoi()673     void ImageRestorer::_updateRoi()
674     {
675         if ( _width < 0 )
676             _width = 0;
677 
678         if ( _height < 0 )
679             _height = 0;
680 
681         if ( _x < 0 ) {
682             const int32_t offset = -_x;
683             _x = 0;
684             _width = _width < offset ? 0 : _width - offset;
685         }
686 
687         if ( _y < 0 ) {
688             const int32_t offset = -_y;
689             _y = 0;
690             _height = _height < offset ? 0 : _height - offset;
691         }
692 
693         if ( _x >= _image.width() || _y >= _image.height() ) {
694             _x = 0;
695             _y = 0;
696             _width = 0;
697             _height = 0;
698             return;
699         }
700 
701         if ( _x + _width > _image.width() ) {
702             const int32_t offsetX = _x + _width - _image.width();
703             if ( offsetX >= _width ) {
704                 _x = 0;
705                 _y = 0;
706                 _width = 0;
707                 _height = 0;
708                 return;
709             }
710             _width -= offsetX;
711         }
712 
713         if ( _y + _height > _image.height() ) {
714             const int32_t offsetY = _y + _height - _image.height();
715             if ( offsetY >= _height ) {
716                 _x = 0;
717                 _y = 0;
718                 _width = 0;
719                 _height = 0;
720                 return;
721             }
722             _height -= offsetY;
723         }
724     }
725 
addShadow(const Sprite & in,const Point & shadowOffset,const uint8_t transformId)726     Sprite addShadow( const Sprite & in, const Point & shadowOffset, const uint8_t transformId )
727     {
728         if ( in.empty() || shadowOffset.x > 0 || shadowOffset.y < 0 )
729             return in;
730 
731         Sprite out = makeShadow( in, shadowOffset, transformId );
732         Blit( in, out, -shadowOffset.x, 0 );
733 
734         return out;
735     }
736 
AddTransparency(Image & image,uint8_t valueToReplace)737     void AddTransparency( Image & image, uint8_t valueToReplace )
738     {
739         ReplaceColorIdByTransformId( image, valueToReplace, 1 );
740     }
741 
AlphaBlit(const Image & in,Image & out,uint8_t alphaValue,bool flip)742     void AlphaBlit( const Image & in, Image & out, uint8_t alphaValue, bool flip )
743     {
744         AlphaBlit( in, 0, 0, out, 0, 0, in.width(), in.height(), alphaValue, flip );
745     }
746 
AlphaBlit(const Image & in,Image & out,int32_t outX,int32_t outY,uint8_t alphaValue,bool flip)747     void AlphaBlit( const Image & in, Image & out, int32_t outX, int32_t outY, uint8_t alphaValue, bool flip )
748     {
749         AlphaBlit( in, 0, 0, out, outX, outY, in.width(), in.height(), alphaValue, flip );
750     }
751 
AlphaBlit(const Image & in,int32_t inX,int32_t inY,Image & out,int32_t outX,int32_t outY,int32_t width,int32_t height,uint8_t alphaValue,bool flip)752     void AlphaBlit( const Image & in, int32_t inX, int32_t inY, Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height, uint8_t alphaValue, bool flip )
753     {
754         if ( alphaValue == 0 ) { // there is nothing we need to do
755             return;
756         }
757 
758         if ( alphaValue == 255 ) {
759             Blit( in, inX, inY, out, outX, outY, width, height, flip );
760             return;
761         }
762 
763         if ( !Verify( in, inX, inY, out, outX, outY, width, height ) ) {
764             return;
765         }
766 
767         // Blitting one image onto another can be done only for image layer so we don't consider transform part of the output image
768         const int32_t widthIn = in.width();
769         const int32_t widthOut = out.width();
770 
771         const uint8_t behindValue = 255 - alphaValue;
772 
773         const uint8_t * gamePalette = fheroes2::getGamePalette();
774 
775         if ( flip ) {
776             const int32_t offsetInY = inY * widthIn + widthIn - 1 - inX;
777             const uint8_t * imageInY = in.image() + offsetInY;
778             const uint8_t * transformInY = in.transform() + offsetInY;
779 
780             const int32_t offsetOutY = outY * widthOut + outX;
781             uint8_t * imageOutY = out.image() + offsetOutY;
782             const uint8_t * imageOutYEnd = imageOutY + height * widthOut;
783 
784             for ( ; imageOutY != imageOutYEnd; imageInY += widthIn, transformInY += widthIn, imageOutY += widthOut ) {
785                 const uint8_t * imageInX = imageInY;
786                 const uint8_t * transformInX = transformInY;
787                 uint8_t * imageOutX = imageOutY;
788                 const uint8_t * imageOutXEnd = imageOutX + width;
789 
790                 for ( ; imageOutX != imageOutXEnd; --imageInX, --transformInX, ++imageOutX ) {
791                     if ( *transformInX == 1 ) { // skip pixel
792                         continue;
793                     }
794 
795                     uint8_t inValue = *imageInX;
796                     if ( *transformInX > 1 ) {
797                         inValue = *( transformTable + ( *transformInX ) * 256 + *imageOutX );
798                     }
799 
800                     const uint8_t * inPAL = gamePalette + inValue * 3;
801                     const uint8_t * outPAL = gamePalette + ( *imageOutX ) * 3;
802 
803                     const uint32_t red = static_cast<uint32_t>( *inPAL ) * alphaValue + static_cast<uint32_t>( *outPAL ) * behindValue;
804                     const uint32_t green = static_cast<uint32_t>( *( inPAL + 1 ) ) * alphaValue + static_cast<uint32_t>( *( outPAL + 1 ) ) * behindValue;
805                     const uint32_t blue = static_cast<uint32_t>( *( inPAL + 2 ) ) * alphaValue + static_cast<uint32_t>( *( outPAL + 2 ) ) * behindValue;
806                     *imageOutX = GetPALColorId( static_cast<uint8_t>( red / 255 ), static_cast<uint8_t>( green / 255 ), static_cast<uint8_t>( blue / 255 ) );
807                 }
808             }
809         }
810         else {
811             const int32_t offsetInY = inY * widthIn + inX;
812             const uint8_t * imageInY = in.image() + offsetInY;
813             const uint8_t * transformInY = in.transform() + offsetInY;
814 
815             uint8_t * imageOutY = out.image() + outY * widthOut + outX;
816             const uint8_t * imageInYEnd = imageInY + height * widthIn;
817 
818             for ( ; imageInY != imageInYEnd; imageInY += widthIn, transformInY += widthIn, imageOutY += widthOut ) {
819                 const uint8_t * imageInX = imageInY;
820                 const uint8_t * transformInX = transformInY;
821                 uint8_t * imageOutX = imageOutY;
822                 const uint8_t * imageInXEnd = imageInX + width;
823 
824                 for ( ; imageInX != imageInXEnd; ++imageInX, ++transformInX, ++imageOutX ) {
825                     if ( *transformInX == 1 ) { // skip pixel
826                         continue;
827                     }
828 
829                     uint8_t inValue = *imageInX;
830                     if ( *transformInX > 1 ) {
831                         inValue = *( transformTable + ( *transformInX ) * 256 + *imageOutX );
832                     }
833 
834                     const uint8_t * inPAL = gamePalette + inValue * 3;
835                     const uint8_t * outPAL = gamePalette + ( *imageOutX ) * 3;
836 
837                     const uint32_t red = static_cast<uint32_t>( *inPAL ) * alphaValue + static_cast<uint32_t>( *outPAL ) * behindValue;
838                     const uint32_t green = static_cast<uint32_t>( *( inPAL + 1 ) ) * alphaValue + static_cast<uint32_t>( *( outPAL + 1 ) ) * behindValue;
839                     const uint32_t blue = static_cast<uint32_t>( *( inPAL + 2 ) ) * alphaValue + static_cast<uint32_t>( *( outPAL + 2 ) ) * behindValue;
840                     *imageOutX = GetPALColorId( static_cast<uint8_t>( red / 255 ), static_cast<uint8_t>( green / 255 ), static_cast<uint8_t>( blue / 255 ) );
841                 }
842             }
843         }
844     }
845 
ApplyPalette(Image & image,const std::vector<uint8_t> & palette)846     void ApplyPalette( Image & image, const std::vector<uint8_t> & palette )
847     {
848         ApplyPalette( image, image, palette );
849     }
850 
ApplyPalette(const Image & in,Image & out,const std::vector<uint8_t> & palette)851     void ApplyPalette( const Image & in, Image & out, const std::vector<uint8_t> & palette )
852     {
853         if ( palette.size() != 256 ) {
854             return;
855         }
856 
857         ApplyRawPalette( in, 0, 0, out, 0, 0, in.width(), in.height(), palette.data() );
858     }
859 
ApplyPalette(Image & image,uint8_t paletteId)860     void ApplyPalette( Image & image, uint8_t paletteId )
861     {
862         ApplyPalette( image, image, paletteId );
863     }
864 
ApplyPalette(const Image & in,Image & out,uint8_t paletteId)865     void ApplyPalette( const Image & in, Image & out, uint8_t paletteId )
866     {
867         if ( paletteId > 15 ) {
868             return;
869         }
870 
871         ApplyRawPalette( in, 0, 0, out, 0, 0, in.width(), in.height(), transformTable + paletteId * 256 );
872     }
873 
ApplyPalette(const Image & in,int32_t inX,int32_t inY,Image & out,int32_t outX,int32_t outY,int32_t width,int32_t height,uint8_t paletteId)874     void ApplyPalette( const Image & in, int32_t inX, int32_t inY, Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height, uint8_t paletteId )
875     {
876         if ( paletteId > 15 ) {
877             return;
878         }
879 
880         ApplyRawPalette( in, inX, inY, out, outX, outY, width, height, transformTable + paletteId * 256 );
881     }
882 
ApplyPalette(const Image & in,int32_t inX,int32_t inY,Image & out,int32_t outX,int32_t outY,int32_t width,int32_t height,const std::vector<uint8_t> & palette)883     void ApplyPalette( const Image & in, int32_t inX, int32_t inY, Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height,
884                        const std::vector<uint8_t> & palette )
885     {
886         if ( palette.size() != 256 ) {
887             return;
888         }
889 
890         ApplyRawPalette( in, inX, inY, out, outX, outY, width, height, palette.data() );
891     }
892 
ApplyAlpha(const Image & in,Image & out,uint8_t alpha)893     void ApplyAlpha( const Image & in, Image & out, uint8_t alpha )
894     {
895         ApplyAlpha( in, 0, 0, out, 0, 0, in.width(), in.height(), alpha );
896     }
897 
ApplyAlpha(const Image & in,int32_t inX,int32_t inY,Image & out,int32_t outX,int32_t outY,int32_t width,int32_t height,uint8_t alpha)898     void ApplyAlpha( const Image & in, int32_t inX, int32_t inY, Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height, uint8_t alpha )
899     {
900         std::vector<uint8_t> palette( 256 );
901 
902         const uint8_t * value = fheroes2::getGamePalette();
903 
904         for ( uint32_t i = 0; i < 256; ++i ) {
905             const uint32_t red = static_cast<uint32_t>( *value ) * alpha / 255;
906             ++value;
907             const uint32_t green = static_cast<uint32_t>( *value ) * alpha / 255;
908             ++value;
909             const uint32_t blue = static_cast<uint32_t>( *value ) * alpha / 255;
910             ++value;
911             palette[i] = GetPALColorId( static_cast<uint8_t>( red ), static_cast<uint8_t>( green ), static_cast<uint8_t>( blue ) );
912         }
913 
914         ApplyPalette( in, inX, inY, out, outX, outY, width, height, palette );
915     }
916 
ApplyTransform(Image & image,int32_t x,int32_t y,int32_t width,int32_t height,uint8_t transformId)917     void ApplyTransform( Image & image, int32_t x, int32_t y, int32_t width, int32_t height, uint8_t transformId )
918     {
919         if ( !Verify( image, x, y, width, height ) )
920             return;
921 
922         const int32_t imageWidth = image.width();
923 
924         uint8_t * imageY = image.image() + y * imageWidth + x;
925         const uint8_t * imageYEnd = imageY + height * imageWidth;
926 
927         if ( image.singleLayer() ) {
928             for ( ; imageY != imageYEnd; imageY += imageWidth ) {
929                 uint8_t * imageX = imageY;
930                 const uint8_t * imageXEnd = imageX + width;
931 
932                 for ( ; imageX != imageXEnd; ++imageX ) {
933                     *imageX = *( transformTable + transformId * 256 + *imageX );
934                 }
935             }
936         }
937         else {
938             const uint8_t * transformY = image.transform() + y * imageWidth + x;
939 
940             for ( ; imageY != imageYEnd; imageY += imageWidth, transformY += imageWidth ) {
941                 uint8_t * imageX = imageY;
942                 const uint8_t * transformX = transformY;
943                 const uint8_t * imageXEnd = imageX + width;
944 
945                 for ( ; imageX != imageXEnd; ++imageX, ++transformX ) {
946                     if ( *transformX == 0 ) {
947                         *imageX = *( transformTable + transformId * 256 + *imageX );
948                     }
949                 }
950             }
951         }
952     }
953 
Blit(const Image & in,Image & out,bool flip)954     void Blit( const Image & in, Image & out, bool flip )
955     {
956         Blit( in, 0, 0, out, 0, 0, in.width(), in.height(), flip );
957     }
958 
Blit(const Image & in,Image & out,int32_t outX,int32_t outY,bool flip)959     void Blit( const Image & in, Image & out, int32_t outX, int32_t outY, bool flip )
960     {
961         Blit( in, 0, 0, out, outX, outY, in.width(), in.height(), flip );
962     }
963 
Blit(const Image & in,int32_t inX,int32_t inY,Image & out,int32_t outX,int32_t outY,int32_t width,int32_t height,bool flip)964     void Blit( const Image & in, int32_t inX, int32_t inY, Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height, bool flip )
965     {
966         if ( in.singleLayer() && out.singleLayer() && !flip ) {
967             Copy( in, inX, inY, out, outX, outY, width, height );
968             return;
969         }
970 
971         if ( !Verify( in, inX, inY, out, outX, outY, width, height ) ) {
972             return;
973         }
974 
975         const int32_t widthIn = in.width();
976         const int32_t widthOut = out.width();
977 
978         if ( flip ) {
979             const int32_t offsetInY = inY * widthIn + widthIn - 1 - inX;
980             const uint8_t * imageInY = in.image() + offsetInY;
981             const uint8_t * transformInY = in.transform() + offsetInY;
982 
983             const int32_t offsetOutY = outY * widthOut + outX;
984             uint8_t * imageOutY = out.image() + offsetOutY;
985             const uint8_t * imageOutYEnd = imageOutY + height * widthOut;
986 
987             if ( out.singleLayer() ) {
988                 assert( !in.singleLayer() );
989                 for ( ; imageOutY != imageOutYEnd; imageInY += widthIn, transformInY += widthIn, imageOutY += widthOut ) {
990                     const uint8_t * imageInX = imageInY;
991                     const uint8_t * transformInX = transformInY;
992                     uint8_t * imageOutX = imageOutY;
993                     const uint8_t * imageOutXEnd = imageOutX + width;
994 
995                     for ( ; imageOutX != imageOutXEnd; --imageInX, --transformInX, ++imageOutX ) {
996                         if ( *transformInX > 0 ) { // apply a transformation
997                             if ( *transformInX != 1 ) { // skip pixel
998                                 *imageOutX = *( transformTable + ( *transformInX ) * 256 + *imageOutX );
999                             }
1000                         }
1001                         else { // copy a pixel
1002                             *imageOutX = *imageInX;
1003                         }
1004                     }
1005                 }
1006             }
1007             else {
1008                 uint8_t * transformOutY = out.transform() + offsetOutY;
1009 
1010                 for ( ; imageOutY != imageOutYEnd; imageInY += widthIn, transformInY += widthIn, imageOutY += widthOut, transformOutY += widthOut ) {
1011                     const uint8_t * imageInX = imageInY;
1012                     const uint8_t * transformInX = transformInY;
1013                     uint8_t * imageOutX = imageOutY;
1014                     uint8_t * transformOutX = transformOutY;
1015                     const uint8_t * imageOutXEnd = imageOutX + width;
1016 
1017                     for ( ; imageOutX != imageOutXEnd; --imageInX, --transformInX, ++imageOutX, ++transformOutX ) {
1018                         if ( *transformInX == 1 ) { // skip pixel
1019                             continue;
1020                         }
1021 
1022                         if ( *transformInX > 0 && *transformOutX == 0 ) { // apply a transformation
1023                             *imageOutX = *( transformTable + ( *transformInX ) * 256 + *imageOutX );
1024                         }
1025                         else { // copy a pixel
1026                             *transformOutX = *transformInX;
1027                             *imageOutX = *imageInX;
1028                         }
1029                     }
1030                 }
1031             }
1032         }
1033         else {
1034             const int32_t offsetInY = inY * widthIn + inX;
1035             const uint8_t * imageInY = in.image() + offsetInY;
1036             const uint8_t * transformInY = in.transform() + offsetInY;
1037 
1038             const int32_t offsetOutY = outY * widthOut + outX;
1039             uint8_t * imageOutY = out.image() + offsetOutY;
1040             const uint8_t * imageInYEnd = imageInY + height * widthIn;
1041 
1042             if ( out.singleLayer() ) {
1043                 assert( !in.singleLayer() );
1044                 for ( ; imageInY != imageInYEnd; imageInY += widthIn, transformInY += widthIn, imageOutY += widthOut ) {
1045                     const uint8_t * imageInX = imageInY;
1046                     const uint8_t * transformInX = transformInY;
1047                     uint8_t * imageOutX = imageOutY;
1048                     const uint8_t * imageInXEnd = imageInX + width;
1049 
1050                     for ( ; imageInX != imageInXEnd; ++imageInX, ++transformInX, ++imageOutX ) {
1051                         if ( *transformInX > 0 ) { // apply a transformation
1052                             if ( *transformInX != 1 ) { // skip pixel
1053                                 *imageOutX = *( transformTable + ( *transformInX ) * 256 + *imageOutX );
1054                             }
1055                         }
1056                         else { // copy a pixel
1057                             *imageOutX = *imageInX;
1058                         }
1059                     }
1060                 }
1061             }
1062             else {
1063                 uint8_t * transformOutY = out.transform() + offsetOutY;
1064 
1065                 for ( ; imageInY != imageInYEnd; imageInY += widthIn, transformInY += widthIn, imageOutY += widthOut, transformOutY += widthOut ) {
1066                     const uint8_t * imageInX = imageInY;
1067                     const uint8_t * transformInX = transformInY;
1068                     uint8_t * imageOutX = imageOutY;
1069                     uint8_t * transformOutX = transformOutY;
1070                     const uint8_t * imageInXEnd = imageInX + width;
1071 
1072                     for ( ; imageInX != imageInXEnd; ++imageInX, ++transformInX, ++imageOutX, ++transformOutX ) {
1073                         if ( *transformInX == 1 ) { // skip pixel
1074                             continue;
1075                         }
1076 
1077                         if ( *transformInX > 0 && *transformOutX == 0 ) { // apply a transformation
1078                             *imageOutX = *( transformTable + ( *transformInX ) * 256 + *imageOutX );
1079                         }
1080                         else { // copy a pixel
1081                             *transformOutX = *transformInX;
1082                             *imageOutX = *imageInX;
1083                         }
1084                     }
1085                 }
1086             }
1087         }
1088     }
1089 
Blit(const Image & in,const Point & inPos,Image & out,const Point & outPos,const Size & size,bool flip)1090     void Blit( const Image & in, const Point & inPos, Image & out, const Point & outPos, const Size & size, bool flip )
1091     {
1092         if ( inPos.x < 0 || inPos.y < 0 )
1093             return;
1094 
1095         Blit( in, inPos.x, inPos.y, out, outPos.x, outPos.y, size.width, size.height, flip );
1096     }
1097 
Copy(const Image & in,Image & out)1098     void Copy( const Image & in, Image & out )
1099     {
1100         out.resize( in.width(), in.height() );
1101         Copy( in, 0, 0, out, 0, 0, in.width(), in.height() );
1102     }
1103 
Copy(const Image & in,int32_t inX,int32_t inY,Image & out,int32_t outX,int32_t outY,int32_t width,int32_t height)1104     void Copy( const Image & in, int32_t inX, int32_t inY, Image & out, int32_t outX, int32_t outY, int32_t width, int32_t height )
1105     {
1106         if ( !Verify( in, inX, inY, out, outX, outY, width, height ) ) {
1107             return;
1108         }
1109 
1110         const int32_t widthIn = in.width();
1111         const int32_t widthOut = out.width();
1112 
1113         const int32_t offsetInY = inY * widthIn + inX;
1114         const uint8_t * imageInY = in.image() + offsetInY;
1115 
1116         const int32_t offsetOutY = outY * widthOut + outX;
1117         uint8_t * imageOutY = out.image() + offsetOutY;
1118         const uint8_t * imageOutYEnd = imageOutY + height * widthOut;
1119 
1120         if ( out.singleLayer() ) {
1121             for ( ; imageOutY != imageOutYEnd; imageInY += widthIn, imageOutY += widthOut ) {
1122                 memcpy( imageOutY, imageInY, static_cast<size_t>( width ) );
1123             }
1124         }
1125         else if ( in.singleLayer() ) {
1126             uint8_t * transformOutY = out.transform() + offsetOutY;
1127 
1128             for ( ; imageOutY != imageOutYEnd; imageInY += widthIn, imageOutY += widthOut, transformOutY += widthOut ) {
1129                 memcpy( imageOutY, imageInY, static_cast<size_t>( width ) );
1130                 std::fill( transformOutY, transformOutY + width, 0 );
1131             }
1132         }
1133         else {
1134             const uint8_t * transformInY = in.transform() + offsetInY;
1135             uint8_t * transformOutY = out.transform() + offsetOutY;
1136 
1137             for ( ; imageOutY != imageOutYEnd; imageInY += widthIn, transformInY += widthIn, imageOutY += widthOut, transformOutY += widthOut ) {
1138                 memcpy( imageOutY, imageInY, static_cast<size_t>( width ) );
1139                 memcpy( transformOutY, transformInY, static_cast<size_t>( width ) );
1140             }
1141         }
1142     }
1143 
CopyTransformLayer(const Image & in,Image & out)1144     void CopyTransformLayer( const Image & in, Image & out )
1145     {
1146         if ( in.empty() || out.empty() || in.singleLayer() || out.singleLayer() || in.width() != out.width() || in.height() != out.height() ) {
1147             assert( 0 );
1148             return;
1149         }
1150         memcpy( out.transform(), in.transform(), in.width() * in.height() );
1151     }
1152 
CreateBlurredImage(const Image & in,int32_t blurRadius)1153     Image CreateBlurredImage( const Image & in, int32_t blurRadius )
1154     {
1155         if ( in.empty() )
1156             return Image();
1157 
1158         if ( blurRadius < 1 )
1159             return in;
1160 
1161         const int32_t width = in.width();
1162         const int32_t height = in.height();
1163         if ( blurRadius > width )
1164             blurRadius = width;
1165         if ( blurRadius > height )
1166             blurRadius = height;
1167 
1168         Image out( width, height );
1169         std::fill( out.transform(), out.transform() + width * height, 0 );
1170 
1171         uint8_t * imageOutY = out.image();
1172         const uint8_t * imageIn = in.image();
1173 
1174         const uint8_t * gamePalette = fheroes2::getGamePalette();
1175 
1176         for ( int32_t y = 0; y < height; ++y, imageOutY += width ) {
1177             uint8_t * imageOutX = imageOutY;
1178 
1179             int32_t startY = y - blurRadius;
1180             int32_t endY = y + blurRadius;
1181             if ( startY < 0 )
1182                 startY = 0;
1183             if ( endY > height )
1184                 endY = height;
1185 
1186             const int32_t roiHeight = endY - startY;
1187 
1188             const uint8_t * imageInStartY = imageIn + startY * width;
1189 
1190             for ( int32_t x = 0; x < width; ++x, ++imageOutX ) {
1191                 int32_t startX = x - blurRadius;
1192                 int32_t endX = x + blurRadius;
1193                 if ( startX < 0 )
1194                     startX = 0;
1195                 if ( endX > width )
1196                     endX = width;
1197 
1198                 const int32_t roiWidth = endX - startX;
1199 
1200                 uint32_t sumRed = 0;
1201                 uint32_t sumGreen = 0;
1202                 uint32_t sumBlue = 0;
1203 
1204                 const uint8_t * imageInY = imageInStartY + startX;
1205                 const uint8_t * imageInYEnd = imageInY + roiHeight * width;
1206 
1207                 for ( ; imageInY != imageInYEnd; imageInY += width ) {
1208                     const uint8_t * imageInX = imageInY;
1209                     const uint8_t * imageInXEnd = imageInX + roiWidth;
1210                     for ( ; imageInX != imageInXEnd; ++imageInX ) {
1211                         const uint8_t * palette = gamePalette + *imageInX * 3;
1212 
1213                         sumRed += ( *palette );
1214                         ++palette;
1215                         sumGreen += ( *palette );
1216                         ++palette;
1217                         sumBlue += ( *palette );
1218                         ++palette;
1219                     }
1220                 }
1221 
1222                 const uint32_t roiSize = static_cast<uint32_t>( roiWidth * roiHeight );
1223 
1224                 *imageOutX
1225                     = GetPALColorId( static_cast<uint8_t>( sumRed / roiSize ), static_cast<uint8_t>( sumGreen / roiSize ), static_cast<uint8_t>( sumBlue / roiSize ) );
1226             }
1227         }
1228 
1229         return out;
1230     }
1231 
CreateContour(const Image & image,uint8_t value)1232     Sprite CreateContour( const Image & image, uint8_t value )
1233     {
1234         const int32_t width = image.width();
1235         const int32_t height = image.height();
1236 
1237         Sprite contour( width, height );
1238         contour.reset();
1239         if ( width < 2 || height < 2 ) {
1240             return contour;
1241         }
1242 
1243         const uint8_t * inY = image.transform();
1244         uint8_t * outImageY = contour.image();
1245         uint8_t * outTransformY = contour.transform();
1246 
1247         const int32_t reducedWidth = width - 1;
1248         const int32_t reducedHeight = height - 1;
1249 
1250         for ( int32_t y = 0; y < height; ++y, inY += width, outImageY += width, outTransformY += width ) {
1251             const uint8_t * inX = inY;
1252 
1253             const bool isNotTopRow = ( y > 0 );
1254             const bool isNotBottomRow = ( y < reducedHeight );
1255 
1256             for ( int32_t x = 0; x < width; ++x, ++inX ) {
1257                 if ( *inX > 0 && *inX < 6 ) { // 1 is to skip, 2 - 5 types of shadows
1258                     if ( ( x > 0 && *( inX - 1 ) == 0 ) || ( x < reducedWidth && *( inX + 1 ) == 0 ) || ( isNotTopRow && *( inX - width ) == 0 )
1259                          || ( isNotBottomRow && *( inX + width ) == 0 ) ) {
1260                         outImageY[x] = value;
1261                         outTransformY[x] = 0;
1262                     }
1263                 }
1264             }
1265         }
1266 
1267         return contour;
1268     }
1269 
Crop(const Image & image,int32_t x,int32_t y,int32_t width,int32_t height)1270     Sprite Crop( const Image & image, int32_t x, int32_t y, int32_t width, int32_t height )
1271     {
1272         if ( image.empty() || width <= 0 || height <= 0 )
1273             return Sprite();
1274 
1275         if ( x < 0 ) {
1276             const int32_t offsetX = -x;
1277             if ( offsetX >= width )
1278                 return Sprite();
1279 
1280             x = 0;
1281             width -= offsetX;
1282         }
1283 
1284         if ( y < 0 ) {
1285             const int32_t offsetY = -y;
1286             if ( offsetY >= height )
1287                 return Sprite();
1288 
1289             y = 0;
1290             height -= offsetY;
1291         }
1292 
1293         if ( x > image.width() || y > image.height() )
1294             return Sprite();
1295 
1296         if ( x + width > image.width() ) {
1297             const int32_t offsetX = x + width - image.width();
1298             width -= offsetX;
1299         }
1300 
1301         if ( y + height > image.height() ) {
1302             const int32_t offsetY = y + height - image.height();
1303             height -= offsetY;
1304         }
1305 
1306         Sprite out( width, height );
1307         if ( image.singleLayer() ) {
1308             out._disableTransformLayer();
1309         }
1310 
1311         Copy( image, x, y, out, 0, 0, width, height );
1312         out.setPosition( x, y );
1313         return out;
1314     }
1315 
DrawBorder(Image & image,uint8_t value,uint32_t skipFactor)1316     void DrawBorder( Image & image, uint8_t value, uint32_t skipFactor )
1317     {
1318         if ( image.empty() || image.width() < 2 || image.height() < 2 ) {
1319             return;
1320         }
1321 
1322         const int32_t width = image.width();
1323         const int32_t height = image.height();
1324 
1325         if ( skipFactor < 2 ) {
1326             // top side
1327             uint8_t * data = image.image();
1328             uint8_t * transform = image.transform();
1329             const uint8_t * dataEnd = data + width;
1330             for ( ; data != dataEnd; ++data, ++transform ) {
1331                 *data = value;
1332                 *transform = 0;
1333             }
1334 
1335             // bottom side
1336             data = image.image() + width * ( height - 1 );
1337             transform = image.transform() + width * ( height - 1 );
1338             dataEnd = data + width;
1339             for ( ; data != dataEnd; ++data, ++transform ) {
1340                 *data = value;
1341                 *transform = 0;
1342             }
1343 
1344             // left side
1345             data = image.image() + width;
1346             transform = image.transform() + width;
1347             dataEnd = data + width * ( height - 2 );
1348             for ( ; data != dataEnd; data += width, transform += width ) {
1349                 *data = value;
1350                 *transform = 0;
1351             }
1352 
1353             // right side
1354             data = image.image() + width + width - 1;
1355             transform = image.transform() + width + width - 1;
1356             dataEnd = data + width * ( height - 2 );
1357             for ( ; data != dataEnd; data += width, transform += width ) {
1358                 *data = value;
1359                 *transform = 0;
1360             }
1361         }
1362         else {
1363             uint32_t counter = 0;
1364 
1365             // top side
1366             uint8_t * data = image.image();
1367             uint8_t * transform = image.transform();
1368             const uint8_t * dataEnd = data + width;
1369             for ( ; data != dataEnd; ++data, ++transform ) {
1370                 if ( counter != skipFactor ) {
1371                     *data = value;
1372                     *transform = 0;
1373                 }
1374                 else {
1375                     counter = 0;
1376                 }
1377                 ++counter;
1378             }
1379 
1380             // right side
1381             data = image.image() + width + width - 1;
1382             transform = image.transform() + width + width - 1;
1383             dataEnd = data + width * ( height - 2 );
1384             for ( ; data != dataEnd; data += width, transform += width ) {
1385                 if ( counter != skipFactor ) {
1386                     *data = value;
1387                     *transform = 0;
1388                 }
1389                 else {
1390                     counter = 0;
1391                 }
1392                 ++counter;
1393             }
1394 
1395             // bottom side
1396             data = image.image() + width * ( height - 1 ) + width - 1;
1397             transform = image.transform() + width * ( height - 1 ) + width - 1;
1398             dataEnd = data - width;
1399             for ( ; data != dataEnd; --data, --transform ) {
1400                 if ( counter != skipFactor ) {
1401                     *data = value;
1402                     *transform = 0;
1403                 }
1404                 else {
1405                     counter = 0;
1406                 }
1407                 ++counter;
1408             }
1409 
1410             // left side
1411             data = image.image() + width * ( height - 2 );
1412             transform = image.transform() + width * ( height - 2 );
1413             dataEnd = image.image();
1414             for ( ; data != dataEnd; data -= width, transform -= width ) {
1415                 if ( counter != skipFactor ) {
1416                     *data = value;
1417                     *transform = 0;
1418                 }
1419                 else {
1420                     counter = 0;
1421                 }
1422                 ++counter;
1423             }
1424         }
1425     }
1426 
DrawLine(Image & image,const Point & start,const Point & end,uint8_t value,const Rect & roi)1427     void DrawLine( Image & image, const Point & start, const Point & end, uint8_t value, const Rect & roi )
1428     {
1429         if ( image.empty() )
1430             return;
1431 
1432         const int32_t width = image.width();
1433         const int32_t height = image.height();
1434 
1435         int32_t x1 = start.x;
1436         int32_t y1 = start.y;
1437         int32_t x2 = end.x;
1438         int32_t y2 = end.y;
1439 
1440         const int32_t dx = std::abs( x2 - x1 );
1441         const int32_t dy = std::abs( y2 - y1 );
1442 
1443         const bool isValidRoi = roi.width > 0 && roi.height > 0;
1444 
1445         const int32_t minX = isValidRoi ? roi.x : 0;
1446         const int32_t minY = isValidRoi ? roi.y : 0;
1447         int32_t maxX = isValidRoi ? roi.x + roi.width : width;
1448         int32_t maxY = isValidRoi ? roi.y + roi.height : height;
1449 
1450         if ( minX >= width || minY >= height )
1451             return;
1452 
1453         if ( maxX >= width )
1454             maxX = width;
1455 
1456         if ( maxY >= height )
1457             maxY = height;
1458 
1459         uint8_t * data = image.image();
1460         uint8_t * transform = image.transform();
1461 
1462         if ( dx > dy ) {
1463             int32_t ns = std::div( dx, 2 ).quot;
1464 
1465             for ( int32_t i = 0; i <= dx; ++i ) {
1466                 if ( x1 >= minX && x1 < maxX && y1 >= minY && y1 < maxY ) {
1467                     const int32_t offset = x1 + y1 * width;
1468                     *( data + offset ) = value;
1469                     *( transform + offset ) = 0;
1470                 }
1471                 x1 < x2 ? ++x1 : --x1;
1472                 ns -= dy;
1473                 if ( ns < 0 ) {
1474                     y1 < y2 ? ++y1 : --y1;
1475                     ns += dx;
1476                 }
1477             }
1478         }
1479         else {
1480             int32_t ns = std::div( dy, 2 ).quot;
1481 
1482             for ( int32_t i = 0; i <= dy; ++i ) {
1483                 if ( x1 >= minX && x1 < maxX && y1 >= minY && y1 < maxY ) {
1484                     const int32_t offset = x1 + y1 * width;
1485                     *( data + offset ) = value;
1486                     *( transform + offset ) = 0;
1487                 }
1488                 y1 < y2 ? ++y1 : --y1;
1489                 ns -= dx;
1490                 if ( ns < 0 ) {
1491                     x1 < x2 ? ++x1 : --x1;
1492                     ns += dy;
1493                 }
1494             }
1495         }
1496     }
1497 
DrawRect(Image & image,const Rect & roi,uint8_t value)1498     void DrawRect( Image & image, const Rect & roi, uint8_t value )
1499     {
1500         if ( image.empty() || roi.width < 1 || roi.height < 1 )
1501             return;
1502 
1503         DrawLine( image, { roi.x, roi.y }, { roi.x + roi.width, roi.y }, value, roi );
1504         DrawLine( image, { roi.x, roi.y }, { roi.x, roi.y + roi.height }, value, roi );
1505         DrawLine( image, { roi.x + roi.width - 1, roi.y }, { roi.x + roi.width - 1, roi.y + roi.height }, value, roi );
1506         DrawLine( image, { roi.x, roi.y + roi.height - 1 }, { roi.x + roi.width, roi.y + roi.height - 1 }, value, roi );
1507     }
1508 
ExtractCommonPattern(const std::vector<Image> & input)1509     Image ExtractCommonPattern( const std::vector<Image> & input )
1510     {
1511         if ( input.empty() )
1512             return Image();
1513 
1514         if ( input.size() == 1 )
1515             return input.front();
1516 
1517         if ( input[0].empty() )
1518             return Image();
1519 
1520         for ( size_t i = 1; i < input.size(); ++i ) {
1521             if ( input[i].width() != input[0].width() || input[i].height() != input[0].height() )
1522                 return Image();
1523         }
1524 
1525         std::vector<const uint8_t *> imageIn( input.size() );
1526         std::vector<const uint8_t *> transformIn( input.size() );
1527 
1528         for ( size_t i = 0; i < input.size(); ++i ) {
1529             imageIn[i] = input[i].image();
1530             transformIn[i] = input[i].transform();
1531         }
1532 
1533         Image out( input[0].width(), input[0].height() );
1534         out.reset();
1535 
1536         uint8_t * imageOut = out.image();
1537         uint8_t * transformOut = out.transform();
1538         const uint8_t * imageOutEnd = imageOut + out.width() * out.height();
1539 
1540         bool isEqual = false;
1541 
1542         for ( ; imageOut != imageOutEnd; ++imageOut, ++transformOut ) {
1543             isEqual = true;
1544 
1545             for ( size_t i = 1; i < input.size(); ++i ) {
1546                 if ( *imageIn[0] != *imageIn[i] || *transformIn[0] != *transformIn[i] ) {
1547                     isEqual = false;
1548                     break;
1549                 }
1550             }
1551 
1552             if ( isEqual ) {
1553                 *imageOut = *imageIn[0];
1554                 *transformOut = *transformIn[0];
1555             }
1556 
1557             for ( size_t i = 0; i < input.size(); ++i ) {
1558                 ++imageIn[i];
1559                 ++transformIn[i];
1560             }
1561         }
1562 
1563         return out;
1564     }
1565 
Fill(Image & image,int32_t x,int32_t y,int32_t width,int32_t height,uint8_t colorId)1566     void Fill( Image & image, int32_t x, int32_t y, int32_t width, int32_t height, uint8_t colorId )
1567     {
1568         if ( !Verify( image, x, y, width, height ) )
1569             return;
1570 
1571         if ( image.width() == width && image.height() == height ) { // we need to fill whole image
1572             image.fill( colorId );
1573             return;
1574         }
1575 
1576         const int32_t imageWidth = image.width();
1577 
1578         uint8_t * imageY = image.image() + y * imageWidth + x;
1579         uint8_t * transformY = image.transform() + y * imageWidth + x;
1580         const uint8_t * imageYEnd = imageY + height * imageWidth;
1581 
1582         for ( ; imageY != imageYEnd; imageY += imageWidth, transformY += imageWidth ) {
1583             std::fill( imageY, imageY + width, colorId );
1584             std::fill( transformY, transformY + width, 0 );
1585         }
1586     }
1587 
FillTransform(Image & image,int32_t x,int32_t y,int32_t width,int32_t height,uint8_t tranformId)1588     void FillTransform( Image & image, int32_t x, int32_t y, int32_t width, int32_t height, uint8_t tranformId )
1589     {
1590         if ( !Verify( image, x, y, width, height ) )
1591             return;
1592 
1593         const int32_t imageWidth = image.width();
1594 
1595         uint8_t * imageY = image.image() + y * imageWidth + x;
1596         uint8_t * transformY = image.transform() + y * imageWidth + x;
1597         const uint8_t * imageYEnd = imageY + height * imageWidth;
1598 
1599         for ( ; imageY != imageYEnd; imageY += imageWidth, transformY += imageWidth ) {
1600             std::fill( imageY, imageY + width, 0 );
1601             std::fill( transformY, transformY + width, tranformId );
1602         }
1603     }
1604 
FilterOnePixelNoise(const Image & input)1605     Image FilterOnePixelNoise( const Image & input )
1606     {
1607         if ( input.width() < 3 || input.height() < 3 ) {
1608             return input;
1609         }
1610 
1611         const int32_t width = input.width();
1612         const int32_t height = input.height();
1613 
1614         Image output( width, height );
1615         output.reset();
1616 
1617         const uint8_t * imageInY = input.image();
1618         const uint8_t * transformInY = input.transform();
1619 
1620         uint8_t * imageOutY = output.image();
1621         uint8_t * transformOutY = output.transform();
1622 
1623         for ( int32_t y = 0; y < height; ++y ) {
1624             const uint8_t * transformInX = transformInY;
1625 
1626             for ( int32_t x = 0; x < width; ++x ) {
1627                 if ( *transformInX == 0 && ( x == 0 || x == width - 1 || *( transformInX - 1 ) == 0 || *( transformInX + 1 ) == 0 )
1628                      && ( y == 0 || y == height - 1 || *( transformInX - width ) == 0 || *( transformInX + width ) == 0 ) ) {
1629                     *( transformOutY + x ) = 0;
1630                     *( imageOutY + x ) = *( imageInY + x );
1631                 }
1632 
1633                 ++transformInX;
1634             }
1635 
1636             imageInY += width;
1637             transformInY += width;
1638 
1639             imageOutY += width;
1640             transformOutY += width;
1641         }
1642 
1643         return output;
1644     }
1645 
FitToRoi(const Image & in,Point & inPos,const Image & out,Point & outPos,Size & outputSize,const Rect & outputRoi)1646     bool FitToRoi( const Image & in, Point & inPos, const Image & out, Point & outPos, Size & outputSize, const Rect & outputRoi )
1647     {
1648         if ( !Validate( out, outputRoi.x, outputRoi.y, outputRoi.width, outputRoi.height ) ) {
1649             return false;
1650         }
1651 
1652         outPos.x -= outputRoi.x;
1653         outPos.y -= outputRoi.y;
1654 
1655         if ( !Verify( inPos.x, inPos.y, outPos.x, outPos.y, outputSize.width, outputSize.height, in.width(), in.height(), outputRoi.width, outputRoi.height ) ) {
1656             return false;
1657         }
1658 
1659         outPos.x += outputRoi.x;
1660         outPos.y += outputRoi.y;
1661 
1662         return true;
1663     }
1664 
Flip(const Image & in,bool horizontally,bool vertically)1665     Image Flip( const Image & in, bool horizontally, bool vertically )
1666     {
1667         if ( in.empty() ) {
1668             return Image();
1669         }
1670 
1671         const int32_t width = in.width();
1672         const int32_t height = in.height();
1673 
1674         Image out( width, height );
1675         if ( !horizontally && !vertically ) {
1676             Copy( in, out );
1677             return out;
1678         }
1679 
1680         uint8_t * imageOutY = out.image();
1681         uint8_t * transformOutY = out.transform();
1682         const uint8_t * imageOutYEnd = imageOutY + width * height;
1683 
1684         if ( horizontally && !vertically ) {
1685             const uint8_t * imageInY = in.image() + width - 1;
1686             const uint8_t * transformInY = in.transform() + width - 1;
1687             for ( ; imageOutY != imageOutYEnd; imageOutY += width, transformOutY += width, imageInY += width, transformInY += width ) {
1688                 uint8_t * imageOutX = imageOutY;
1689                 uint8_t * transformOutX = transformOutY;
1690                 const uint8_t * imageOutXEnd = imageOutX + width;
1691                 const uint8_t * imageInX = imageInY;
1692                 const uint8_t * transformInX = transformInY;
1693 
1694                 for ( ; imageOutX != imageOutXEnd; ++imageOutX, ++transformOutX, --imageInX, --transformInX ) {
1695                     *imageOutX = *imageInX;
1696                     *transformOutX = *transformInX;
1697                 }
1698             }
1699         }
1700         else if ( !horizontally && vertically ) {
1701             const int32_t offsetIn = ( height - 1 ) * width;
1702             const uint8_t * imageInY = in.image() + offsetIn;
1703             const uint8_t * transformInY = in.transform() + offsetIn;
1704             for ( ; imageOutY != imageOutYEnd; imageOutY += width, transformOutY += width, imageInY -= width, transformInY -= width ) {
1705                 memcpy( imageOutY, imageInY, static_cast<size_t>( width ) );
1706                 memcpy( transformOutY, transformInY, static_cast<size_t>( width ) );
1707             }
1708         }
1709         else {
1710             const int32_t offsetIn = ( height - 1 ) * width + width - 1;
1711             const uint8_t * imageInY = in.image() + offsetIn;
1712             const uint8_t * transformInY = in.transform() + offsetIn;
1713             for ( ; imageOutY != imageOutYEnd; imageOutY += width, transformOutY += width, imageInY -= width, transformInY -= width ) {
1714                 uint8_t * imageOutX = imageOutY;
1715                 uint8_t * transformOutX = transformOutY;
1716                 const uint8_t * imageOutXEnd = imageOutX + width;
1717                 const uint8_t * imageInX = imageInY;
1718                 const uint8_t * transformInX = transformInY;
1719 
1720                 for ( ; imageOutX != imageOutXEnd; ++imageOutX, ++transformOutX, --imageInX, --transformInX ) {
1721                     *imageOutX = *imageInX;
1722                     *transformOutX = *transformInX;
1723                 }
1724             }
1725         }
1726 
1727         return out;
1728     }
1729 
GetActiveROI(const Image & image,const uint8_t minTransformValue)1730     Rect GetActiveROI( const Image & image, const uint8_t minTransformValue )
1731     {
1732         if ( image.empty() )
1733             return Rect();
1734 
1735         const int32_t width = image.width();
1736         const int32_t height = image.height();
1737 
1738         Rect area( -1, -1, -1, -1 );
1739 
1740         // Top border
1741         const uint8_t * inY = image.transform();
1742         for ( int32_t y = 0; y < height; ++y, inY += width ) {
1743             const uint8_t * inX = inY;
1744 
1745             for ( int32_t x = 0; x < width; ++x, ++inX ) {
1746                 if ( *inX == 0 || *inX >= minTransformValue ) {
1747                     area.y = y;
1748                     break;
1749                 }
1750             }
1751 
1752             if ( area.y >= 0 )
1753                 break;
1754         }
1755 
1756         if ( area.y < 0 )
1757             return Rect();
1758 
1759         // Bottom border
1760         inY = image.transform() + width * ( height - 1 );
1761         for ( int32_t y = height - 1; y > area.y; --y, inY -= width ) {
1762             const uint8_t * inX = inY;
1763 
1764             for ( int32_t x = 0; x < width; ++x, ++inX ) {
1765                 if ( *inX == 0 || *inX >= minTransformValue ) {
1766                     area.height = y - area.y + 1;
1767                     break;
1768                 }
1769             }
1770 
1771             if ( area.height >= 0 )
1772                 break;
1773         }
1774 
1775         // Left border
1776         const uint8_t * inX = image.transform() + width * area.y;
1777         for ( int32_t x = 0; x < width; ++x, ++inX ) {
1778             inY = inX;
1779 
1780             for ( int32_t y = 0; y < area.height; ++y, inY += width ) {
1781                 if ( *inY == 0 || *inY >= minTransformValue ) {
1782                     area.x = x;
1783                     break;
1784                 }
1785             }
1786 
1787             if ( area.x >= 0 )
1788                 break;
1789         }
1790 
1791         // Right border
1792         inX = image.transform() + width * area.y + width - 1;
1793         for ( int32_t x = width - 1; x >= area.x; --x, --inX ) {
1794             inY = inX;
1795 
1796             for ( int32_t y = 0; y < area.height; ++y, inY += width ) {
1797                 if ( *inY == 0 || *inY >= minTransformValue ) {
1798                     area.width = x - area.x + 1;
1799                     break;
1800                 }
1801             }
1802 
1803             if ( area.width >= 0 )
1804                 break;
1805         }
1806 
1807         return area;
1808     }
1809 
GetColorId(uint8_t red,uint8_t green,uint8_t blue)1810     uint8_t GetColorId( uint8_t red, uint8_t green, uint8_t blue )
1811     {
1812         return GetPALColorId( red / 4, green / 4, blue / 4 );
1813     }
1814 
makeShadow(const Sprite & in,const Point & shadowOffset,const uint8_t transformId)1815     Sprite makeShadow( const Sprite & in, const Point & shadowOffset, const uint8_t transformId )
1816     {
1817         if ( in.empty() || shadowOffset.x > 0 || shadowOffset.y < 0 )
1818             return Sprite();
1819 
1820         const int32_t width = in.width();
1821         const int32_t height = in.height();
1822 
1823         // Shadow has (-x, +y) offset.
1824         Sprite out( width - shadowOffset.x, height + shadowOffset.y, in.x() + shadowOffset.x, in.y() );
1825         out.reset();
1826 
1827         const int32_t widthOut = out.width();
1828 
1829         const uint8_t * transformInY = in.transform();
1830         const uint8_t * transformInYEnd = transformInY + width * height;
1831         uint8_t * transformOutY = out.transform() + shadowOffset.y * widthOut;
1832 
1833         for ( ; transformInY != transformInYEnd; transformInY += width, transformOutY += widthOut ) {
1834             const uint8_t * transformInX = transformInY;
1835             uint8_t * transformOutX = transformOutY;
1836             const uint8_t * transformInXEnd = transformInX + width;
1837 
1838             for ( ; transformInX != transformInXEnd; ++transformInX, ++transformOutX ) {
1839                 if ( *transformInX == 0 ) {
1840                     *transformOutX = transformId;
1841                 }
1842             }
1843         }
1844 
1845         return out;
1846     }
1847 
ReplaceColorId(Image & image,uint8_t oldColorId,uint8_t newColorId)1848     void ReplaceColorId( Image & image, uint8_t oldColorId, uint8_t newColorId )
1849     {
1850         if ( image.empty() )
1851             return;
1852 
1853         uint8_t * data = image.image();
1854         const uint8_t * dataEnd = data + image.width() * image.height();
1855 
1856         for ( ; data != dataEnd; ++data ) {
1857             if ( *data == oldColorId ) {
1858                 *data = newColorId;
1859             }
1860         }
1861     }
1862 
ReplaceColorIdByTransformId(Image & image,uint8_t colorId,uint8_t transformId)1863     void ReplaceColorIdByTransformId( Image & image, uint8_t colorId, uint8_t transformId )
1864     {
1865         if ( transformId > 15 )
1866             return;
1867 
1868         const int32_t width = image.width();
1869         const int32_t height = image.height();
1870 
1871         const uint8_t * imageIn = image.image();
1872         uint8_t * transformIn = image.transform();
1873         const uint8_t * imageInEnd = imageIn + height * width;
1874         for ( ; imageIn != imageInEnd; ++imageIn, ++transformIn ) {
1875             if ( *transformIn == 0 && *imageIn == colorId ) { // modify pixels with tranform value 0
1876                 *transformIn = transformId;
1877             }
1878         }
1879     }
1880 
Resize(const Image & in,Image & out,const bool isSubpixelAccuracy)1881     void Resize( const Image & in, Image & out, const bool isSubpixelAccuracy )
1882     {
1883         if ( in.empty() || out.empty() ) {
1884             return;
1885         }
1886 
1887         Resize( in, 0, 0, in.width(), in.height(), out, 0, 0, out.width(), out.height(), isSubpixelAccuracy );
1888     }
1889 
Resize(const Image & in,const int32_t inX,const int32_t inY,const int32_t widthRoiIn,const int32_t heightRoiIn,Image & out,const int32_t outX,const int32_t outY,const int32_t widthRoiOut,const int32_t heightRoiOut,const bool isSubpixelAccuracy)1890     void Resize( const Image & in, const int32_t inX, const int32_t inY, const int32_t widthRoiIn, const int32_t heightRoiIn, Image & out, const int32_t outX,
1891                  const int32_t outY, const int32_t widthRoiOut, const int32_t heightRoiOut, const bool isSubpixelAccuracy )
1892     {
1893         if ( !Validate( in, inX, inY, widthRoiIn, heightRoiIn ) || !Validate( out, outX, outY, widthRoiOut, heightRoiOut ) )
1894             return;
1895 
1896         if ( widthRoiIn == widthRoiOut && heightRoiIn == heightRoiOut ) {
1897             Copy( in, inX, inY, out, outX, outY, widthRoiIn, heightRoiIn );
1898             return;
1899         }
1900 
1901         const int32_t widthIn = in.width();
1902         const int32_t widthOut = out.width();
1903 
1904         const int32_t offsetInY = inY * widthIn + inX;
1905         const int32_t offsetOutY = outY * widthOut + outX;
1906 
1907         const uint8_t * imageInY = in.image() + offsetInY;
1908         uint8_t * imageOutY = out.image() + offsetOutY;
1909 
1910         if ( isSubpixelAccuracy ) {
1911             std::vector<double> positionX( widthRoiOut );
1912             std::vector<double> positionY( heightRoiOut );
1913             for ( int32_t x = 0; x < widthRoiOut; ++x )
1914                 positionX[x] = static_cast<double>( x * widthRoiIn ) / widthRoiOut;
1915             for ( int32_t y = 0; y < heightRoiOut; ++y )
1916                 positionY[y] = static_cast<double>( y * heightRoiIn ) / heightRoiOut;
1917 
1918             const uint8_t * gamePalette = fheroes2::getGamePalette();
1919 
1920             if ( in.singleLayer() && out.singleLayer() ) {
1921                 for ( int32_t y = 0; y < heightRoiOut; ++y, imageOutY += widthOut ) {
1922                     const double posY = positionY[y];
1923                     const int32_t startY = static_cast<int32_t>( posY );
1924                     const double coeffY = posY - startY;
1925 
1926                     uint8_t * imageOutX = imageOutY;
1927 
1928                     for ( int32_t x = 0; x < widthRoiOut; ++x, ++imageOutX ) {
1929                         const double posX = positionX[x];
1930                         const int32_t startX = static_cast<int32_t>( posX );
1931                         const int32_t offsetIn = startY * widthIn + startX;
1932 
1933                         const uint8_t * imageInX = imageInY + offsetIn;
1934 
1935                         if ( posX < widthRoiIn - 1 && posY < heightRoiIn - 1 ) {
1936                             const double coeffX = posX - startX;
1937                             const double coeff1 = ( 1 - coeffX ) * ( 1 - coeffY );
1938                             const double coeff2 = coeffX * ( 1 - coeffY );
1939                             const double coeff3 = ( 1 - coeffX ) * coeffY;
1940                             const double coeff4 = coeffX * coeffY;
1941 
1942                             const uint8_t * id1 = gamePalette + static_cast<uint32_t>( *imageInX ) * 3;
1943                             const uint8_t * id2 = gamePalette + static_cast<uint32_t>( *( imageInX + 1 ) ) * 3;
1944                             const uint8_t * id3 = gamePalette + static_cast<uint32_t>( *( imageInX + widthIn ) ) * 3;
1945                             const uint8_t * id4 = gamePalette + static_cast<uint32_t>( *( imageInX + widthIn + 1 ) ) * 3;
1946 
1947                             const double red = *id1 * coeff1 + *id2 * coeff2 + *id3 * coeff3 + *id4 * coeff4 + 0.5;
1948                             const double green = *( id1 + 1 ) * coeff1 + *( id2 + 1 ) * coeff2 + *( id3 + 1 ) * coeff3 + *( id4 + 1 ) * coeff4 + 0.5;
1949                             const double blue = *( id1 + 2 ) * coeff1 + *( id2 + 2 ) * coeff2 + *( id3 + 2 ) * coeff3 + *( id4 + 2 ) * coeff4 + 0.5;
1950 
1951                             *imageOutX = GetPALColorId( static_cast<uint8_t>( red ), static_cast<uint8_t>( green ), static_cast<uint8_t>( blue ) );
1952                         }
1953                         else {
1954                             *imageOutX = *imageInX;
1955                         }
1956                     }
1957                 }
1958             }
1959             else {
1960                 const uint8_t * transformInY = in.transform() + offsetInY;
1961                 uint8_t * transformOutY = out.transform() + offsetOutY;
1962 
1963                 for ( int32_t y = 0; y < heightRoiOut; ++y, imageOutY += widthOut, transformOutY += widthOut ) {
1964                     const double posY = positionY[y];
1965                     const int32_t startY = static_cast<int32_t>( posY );
1966                     const double coeffY = posY - startY;
1967 
1968                     uint8_t * imageOutX = imageOutY;
1969                     uint8_t * transformOutX = transformOutY;
1970 
1971                     for ( int32_t x = 0; x < widthRoiOut; ++x, ++imageOutX, ++transformOutX ) {
1972                         const double posX = positionX[x];
1973                         const int32_t startX = static_cast<int32_t>( posX );
1974                         const int32_t offsetIn = startY * widthIn + startX;
1975 
1976                         const uint8_t * imageInX = imageInY + offsetIn;
1977                         const uint8_t * transformInX = transformInY + offsetIn;
1978 
1979                         if ( posX < widthIn - 1 && posY < heightRoiIn - 1 ) {
1980                             if ( *transformInX == 0 && *( transformInX + 1 ) == 0 && *( transformInX + widthRoiIn ) == 0 && *( transformInX + widthRoiIn + 1 ) == 0 ) {
1981                                 const double coeffX = posX - startX;
1982                                 const double coeff1 = ( 1 - coeffX ) * ( 1 - coeffY );
1983                                 const double coeff2 = coeffX * ( 1 - coeffY );
1984                                 const double coeff3 = ( 1 - coeffX ) * coeffY;
1985                                 const double coeff4 = coeffX * coeffY;
1986 
1987                                 const uint8_t * id1 = gamePalette + static_cast<uint32_t>( *imageInX ) * 3;
1988                                 const uint8_t * id2 = gamePalette + static_cast<uint32_t>( *( imageInX + 1 ) ) * 3;
1989                                 const uint8_t * id3 = gamePalette + static_cast<uint32_t>( *( imageInX + widthIn ) ) * 3;
1990                                 const uint8_t * id4 = gamePalette + static_cast<uint32_t>( *( imageInX + widthIn + 1 ) ) * 3;
1991 
1992                                 const double red = *id1 * coeff1 + *id2 * coeff2 + *id3 * coeff3 + *id4 * coeff4 + 0.5;
1993                                 const double green = *( id1 + 1 ) * coeff1 + *( id2 + 1 ) * coeff2 + *( id3 + 1 ) * coeff3 + *( id4 + 1 ) * coeff4 + 0.5;
1994                                 const double blue = *( id1 + 2 ) * coeff1 + *( id2 + 2 ) * coeff2 + *( id3 + 2 ) * coeff3 + *( id4 + 2 ) * coeff4 + 0.5;
1995 
1996                                 *imageOutX = GetPALColorId( static_cast<uint8_t>( red ), static_cast<uint8_t>( green ), static_cast<uint8_t>( blue ) );
1997                             }
1998                             else {
1999                                 *imageOutX = *imageInX;
2000                             }
2001                         }
2002                         else {
2003                             *imageOutX = *imageInX;
2004                         }
2005 
2006                         *transformOutX = *transformInX;
2007                     }
2008                 }
2009             }
2010         }
2011         else {
2012             const uint8_t * imageOutYEnd = imageOutY + widthOut * heightRoiOut;
2013             int32_t idY = 0;
2014 
2015             // Precalculation of X position
2016             std::vector<int32_t> positionX( widthRoiOut );
2017             for ( int32_t x = 0; x < widthRoiOut; ++x )
2018                 positionX[x] = ( x * widthRoiIn ) / widthRoiOut;
2019 
2020             if ( in.singleLayer() && out.singleLayer() ) {
2021                 for ( ; imageOutY != imageOutYEnd; imageOutY += widthOut, ++idY ) {
2022                     uint8_t * imageOutX = imageOutY;
2023                     const uint8_t * imageOutXEnd = imageOutX + widthRoiOut;
2024 
2025                     const int32_t offset = ( ( idY * heightRoiIn ) / heightRoiOut ) * widthIn;
2026                     const uint8_t * imageInX = imageInY + offset;
2027                     const int32_t * idX = positionX.data();
2028 
2029                     for ( ; imageOutX != imageOutXEnd; ++imageOutX, ++idX ) {
2030                         *imageOutX = *( imageInX + ( *idX ) );
2031                     }
2032                 }
2033             }
2034             else {
2035                 const uint8_t * transformInY = in.transform() + offsetInY;
2036                 uint8_t * transformOutY = out.transform() + offsetOutY;
2037 
2038                 for ( ; imageOutY != imageOutYEnd; imageOutY += widthOut, transformOutY += widthOut, ++idY ) {
2039                     uint8_t * imageOutX = imageOutY;
2040                     uint8_t * transformOutX = transformOutY;
2041                     const uint8_t * imageOutXEnd = imageOutX + widthRoiOut;
2042 
2043                     const int32_t offset = ( ( idY * heightRoiIn ) / heightRoiOut ) * widthIn;
2044                     const uint8_t * imageInX = imageInY + offset;
2045                     const uint8_t * transformInX = transformInY + offset;
2046                     const int32_t * idX = positionX.data();
2047 
2048                     for ( ; imageOutX != imageOutXEnd; ++imageOutX, ++transformOutX, ++idX ) {
2049                         *imageOutX = *( imageInX + ( *idX ) );
2050                         *transformOutX = *( transformInX + ( *idX ) );
2051                     }
2052                 }
2053             }
2054         }
2055     }
2056 
SetPixel(Image & image,int32_t x,int32_t y,uint8_t value)2057     void SetPixel( Image & image, int32_t x, int32_t y, uint8_t value )
2058     {
2059         if ( image.empty() || x >= image.width() || y >= image.height() || x < 0 || y < 0 ) {
2060             return;
2061         }
2062 
2063         const int32_t offset = y * image.width() + x;
2064         *( image.image() + offset ) = value;
2065         *( image.transform() + offset ) = 0;
2066     }
2067 
SetPixel(Image & image,const std::vector<Point> & points,uint8_t value)2068     void SetPixel( Image & image, const std::vector<Point> & points, uint8_t value )
2069     {
2070         if ( image.empty() ) {
2071             return;
2072         }
2073 
2074         const int32_t width = image.width();
2075         const int32_t height = image.height();
2076 
2077         for ( const Point & point : points ) {
2078             if ( point.x >= width || point.y >= height || point.x < 0 || point.y < 0 ) {
2079                 continue;
2080             }
2081 
2082             const int32_t offset = point.y * width + point.x;
2083             *( image.image() + offset ) = value;
2084             *( image.transform() + offset ) = 0;
2085         }
2086     }
2087 
SetTransformPixel(Image & image,int32_t x,int32_t y,uint8_t value)2088     void SetTransformPixel( Image & image, int32_t x, int32_t y, uint8_t value )
2089     {
2090         if ( image.empty() || x >= image.width() || y >= image.height() || x < 0 || y < 0 ) {
2091             return;
2092         }
2093 
2094         const int32_t offset = y * image.width() + x;
2095         *( image.image() + offset ) = 0;
2096         *( image.transform() + offset ) = value;
2097     }
2098 
Stretch(const Image & in,int32_t inX,int32_t inY,int32_t widthIn,int32_t heightIn,int32_t widthOut,int32_t heightOut)2099     Image Stretch( const Image & in, int32_t inX, int32_t inY, int32_t widthIn, int32_t heightIn, int32_t widthOut, int32_t heightOut )
2100     {
2101         if ( !Validate( in, inX, inY, widthIn, heightIn ) || widthOut <= 0 || heightOut <= 0 ) {
2102             return Image();
2103         }
2104 
2105         Image out( widthOut, heightOut );
2106 
2107         const int32_t minWidth = widthIn < widthOut ? widthIn : widthOut;
2108         const int32_t minHeight = heightIn < heightOut ? heightIn : heightOut;
2109 
2110         const int32_t cornerWidth = minWidth / 3;
2111         const int32_t cornerHeight = minHeight / 3;
2112         const int32_t cornerX = inX + ( widthIn - cornerWidth ) / 2;
2113         const int32_t cornerY = inY + ( heightIn - cornerHeight ) / 2;
2114         const int32_t bodyWidth = minWidth - 2 * cornerWidth;
2115         const int32_t bodyHeight = minHeight - 2 * cornerHeight;
2116 
2117         const int32_t outX = ( widthOut - ( widthOut / bodyWidth ) * bodyWidth ) / 2;
2118         const int32_t outY = ( heightOut - ( heightOut / bodyHeight ) * bodyHeight ) / 2;
2119 
2120         // Create internal area
2121         if ( bodyWidth < widthOut && bodyHeight < heightOut ) {
2122             for ( int32_t y = 0; y < ( heightOut / bodyHeight ); ++y ) {
2123                 for ( int32_t x = 0; x < ( widthOut / bodyWidth ); ++x ) {
2124                     Copy( in, cornerX, cornerY, out, outX + x * bodyWidth, outY + y * bodyHeight, bodyWidth, bodyHeight );
2125                 }
2126             }
2127         }
2128 
2129         // Create sides
2130         // top and bottom side
2131         for ( int32_t x = 0; x < ( widthOut / bodyWidth ); ++x ) {
2132             const int32_t offsetX = outX + x * bodyWidth;
2133             Copy( in, cornerX, inY, out, offsetX, 0, bodyWidth, cornerHeight );
2134             Copy( in, cornerX, inY + heightIn - cornerHeight, out, offsetX, heightOut - cornerHeight, bodyWidth, cornerHeight );
2135         }
2136 
2137         // left and right sides
2138         for ( int32_t y = 0; y < ( heightOut / bodyHeight ); ++y ) {
2139             const int32_t offsetY = outY + y * bodyHeight;
2140             Copy( in, inX, cornerY, out, 0, offsetY, cornerWidth, bodyHeight );
2141             Copy( in, inX + widthIn - cornerWidth, cornerY, out, widthOut - cornerWidth, offsetY, cornerWidth, bodyHeight );
2142         }
2143 
2144         // Create corners
2145         // top-left corner
2146         Copy( in, inX, inY, out, 0, 0, cornerWidth, cornerHeight );
2147 
2148         // top-right corner
2149         Copy( in, inX + widthIn - cornerWidth, inY, out, widthOut - cornerWidth, 0, cornerWidth, cornerHeight );
2150 
2151         // bottom-left corner
2152         Copy( in, inX, inY + heightIn - cornerHeight, out, 0, heightOut - cornerHeight, cornerWidth, cornerHeight );
2153 
2154         // bottom-right corner
2155         Copy( in, inX + widthIn - cornerWidth, inY + heightIn - cornerHeight, out, widthOut - cornerWidth, heightOut - cornerHeight, cornerWidth, cornerHeight );
2156 
2157         return out;
2158     }
2159 
Transpose(const Image & in,Image & out)2160     void Transpose( const Image & in, Image & out )
2161     {
2162         assert( !out.empty() );
2163 
2164         if ( in.empty() || in.width() != out.height() || in.height() != out.width() ) {
2165             out.reset();
2166             return;
2167         }
2168         const int32_t width = in.width();
2169         const int32_t height = in.height();
2170 
2171         const uint8_t * imageInY = in.image();
2172         const uint8_t * imageInYEnd = imageInY + width * height;
2173         uint8_t * imageOutX = out.image();
2174 
2175         const uint8_t * transformInY = in.transform();
2176         uint8_t * transformOutX = out.transform();
2177 
2178         for ( ; imageInY != imageInYEnd; imageInY += width, transformInY += width, ++imageOutX, ++transformOutX ) {
2179             const uint8_t * imageInX = imageInY;
2180             const uint8_t * imageInXEnd = imageInX + width;
2181             uint8_t * imageOutY = imageOutX;
2182 
2183             const uint8_t * transformInX = transformInY;
2184             uint8_t * transformOutY = transformOutX;
2185             for ( ; imageInX != imageInXEnd; ++imageInX, ++transformInX, imageOutY += height, transformOutY += height ) {
2186                 *imageOutY = *imageInX;
2187                 *transformOutY = *transformInX;
2188             }
2189         }
2190     }
2191 
updateShadow(Image & image,const Point & shadowOffset,const uint8_t transformId)2192     void updateShadow( Image & image, const Point & shadowOffset, const uint8_t transformId )
2193     {
2194         if ( image.empty() || shadowOffset.x > 0 || shadowOffset.y < 0 || ( -shadowOffset.x >= image.width() ) || ( shadowOffset.y >= image.height() ) )
2195             return;
2196 
2197         const int32_t width = image.width() + shadowOffset.x;
2198         const int32_t height = image.height() - shadowOffset.y;
2199 
2200         const int32_t imageWidth = image.width();
2201 
2202         const uint8_t * transformInY = image.transform() - shadowOffset.x;
2203         uint8_t * transformOutY = image.transform() + imageWidth * shadowOffset.y;
2204         const uint8_t * transformOutYEnd = transformOutY + imageWidth * height;
2205 
2206         for ( ; transformOutY != transformOutYEnd; transformInY += imageWidth, transformOutY += imageWidth ) {
2207             const uint8_t * transformInX = transformInY;
2208             uint8_t * transformOutX = transformOutY;
2209             const uint8_t * transformOutXEnd = transformOutX + width;
2210 
2211             for ( ; transformOutX != transformOutXEnd; ++transformInX, ++transformOutX ) {
2212                 if ( *transformInX == 0 && *transformOutX == 1 ) {
2213                     *transformOutX = transformId;
2214                 }
2215             }
2216         }
2217     }
2218 }
2219