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