2013-03-17 07:45:21 +08:00
//
// ZoneMinder Image Class Implementation, $Date$, $Revision$
// Copyright (C) 2001-2008 Philip Coombes
2019-09-17 22:31:48 +08:00
//
2013-03-17 07:45:21 +08:00
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License
// as published by the Free Software Foundation; either version 2
// of the License, or (at your option) any later version.
2019-09-17 22:31:48 +08:00
//
2013-03-17 07:45:21 +08:00
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
2019-09-17 22:31:48 +08:00
//
2013-03-17 07:45:21 +08:00
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
2016-12-26 23:23:16 +08:00
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
2019-09-17 22:31:48 +08:00
//
2021-02-04 11:47:28 +08:00
2013-03-17 07:45:21 +08:00
# include "zm_image.h"
2021-02-04 11:47:28 +08:00
# include "zm_font.h"
# include "zm_poly.h"
# include "zm_utils.h"
2021-02-06 22:41:56 +08:00
# include <algorithm>
2019-05-17 03:37:03 +08:00
# include <fcntl.h>
2013-03-17 07:45:21 +08:00
# include <sys/stat.h>
2021-03-04 06:48:02 +08:00
# include <unistd.h>
2013-03-17 07:45:21 +08:00
2016-09-01 08:36:14 +08:00
static unsigned char y_table_global [ ] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 8 , 9 , 10 , 11 , 12 , 13 , 15 , 16 , 17 , 18 , 19 , 20 , 22 , 23 , 24 , 25 , 26 , 27 , 29 , 30 , 31 , 32 , 33 , 34 , 36 , 37 , 38 , 39 , 40 , 41 , 43 , 44 , 45 , 46 , 47 , 48 , 50 , 51 , 52 , 53 , 54 , 55 , 57 , 58 , 59 , 60 , 61 , 62 , 64 , 65 , 66 , 67 , 68 , 69 , 71 , 72 , 73 , 74 , 75 , 76 , 78 , 79 , 80 , 81 , 82 , 83 , 85 , 86 , 87 , 88 , 89 , 90 , 91 , 93 , 94 , 95 , 96 , 97 , 98 , 100 , 101 , 102 , 103 , 104 , 105 , 107 , 108 , 109 , 110 , 111 , 112 , 114 , 115 , 116 , 117 , 118 , 119 , 121 , 122 , 123 , 124 , 125 , 126 , 128 , 129 , 130 , 131 , 132 , 133 , 135 , 136 , 137 , 138 , 139 , 140 , 142 , 143 , 144 , 145 , 146 , 147 , 149 , 150 , 151 , 152 , 153 , 154 , 156 , 157 , 158 , 159 , 160 , 161 , 163 , 164 , 165 , 166 , 167 , 168 , 170 , 171 , 172 , 173 , 174 , 175 , 176 , 178 , 179 , 180 , 181 , 182 , 183 , 185 , 186 , 187 , 188 , 189 , 190 , 192 , 193 , 194 , 195 , 196 , 197 , 199 , 200 , 201 , 202 , 203 , 204 , 206 , 207 , 208 , 209 , 210 , 211 , 213 , 214 , 215 , 216 , 217 , 218 , 220 , 221 , 222 , 223 , 224 , 225 , 227 , 228 , 229 , 230 , 231 , 232 , 234 , 235 , 236 , 237 , 238 , 239 , 241 , 242 , 243 , 244 , 245 , 246 , 248 , 249 , 250 , 251 , 252 , 253 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 , 255 } ;
static signed char uv_table_global [ ] = { - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 127 , - 125 , - 124 , - 123 , - 122 , - 121 , - 120 , - 119 , - 117 , - 116 , - 115 , - 114 , - 113 , - 112 , - 111 , - 109 , - 108 , - 107 , - 106 , - 105 , - 104 , - 103 , - 102 , - 100 , - 99 , - 98 , - 97 , - 96 , - 95 , - 94 , - 92 , - 91 , - 90 , - 89 , - 88 , - 87 , - 86 , - 85 , - 83 , - 82 , - 81 , - 80 , - 79 , - 78 , - 77 , - 75 , - 74 , - 73 , - 72 , - 71 , - 70 , - 69 , - 68 , - 66 , - 65 , - 64 , - 63 , - 62 , - 61 , - 60 , - 58 , - 57 , - 56 , - 55 , - 54 , - 53 , - 52 , - 51 , - 49 , - 48 , - 47 , - 46 , - 45 , - 44 , - 43 , - 41 , - 40 , - 39 , - 38 , - 37 , - 36 , - 35 , - 34 , - 32 , - 31 , - 30 , - 29 , - 28 , - 27 , - 26 , - 24 , - 23 , - 22 , - 21 , - 20 , - 19 , - 18 , - 17 , - 15 , - 14 , - 13 , - 12 , - 11 , - 10 , - 9 , - 7 , - 6 , - 5 , - 4 , - 3 , - 2 , - 1 , 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , 17 , 18 , 19 , 20 , 21 , 22 , 23 , 24 , 26 , 27 , 28 , 29 , 30 , 31 , 32 , 34 , 35 , 36 , 37 , 38 , 39 , 40 , 41 , 43 , 44 , 45 , 46 , 47 , 48 , 49 , 51 , 52 , 53 , 54 , 55 , 56 , 57 , 58 , 60 , 61 , 62 , 63 , 64 , 65 , 66 , 68 , 69 , 70 , 71 , 72 , 73 , 74 , 75 , 77 , 78 , 79 , 80 , 81 , 82 , 83 , 85 , 86 , 87 , 88 , 89 , 90 , 91 , 92 , 94 , 95 , 96 , 97 , 98 , 99 , 100 , 102 , 103 , 104 , 105 , 106 , 107 , 108 , 109 , 111 , 112 , 113 , 114 , 115 , 116 , 117 , 119 , 120 , 121 , 122 , 123 , 124 , 125 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 , 127 } ;
static short r_v_table_global [ ] = { - 179 , - 178 , - 176 , - 175 , - 173 , - 172 , - 171 , - 169 , - 168 , - 166 , - 165 , - 164 , - 162 , - 161 , - 159 , - 158 , - 157 , - 155 , - 154 , - 152 , - 151 , - 150 , - 148 , - 147 , - 145 , - 144 , - 143 , - 141 , - 140 , - 138 , - 137 , - 135 , - 134 , - 133 , - 131 , - 130 , - 128 , - 127 , - 126 , - 124 , - 123 , - 121 , - 120 , - 119 , - 117 , - 116 , - 114 , - 113 , - 112 , - 110 , - 109 , - 107 , - 106 , - 105 , - 103 , - 102 , - 100 , - 99 , - 98 , - 96 , - 95 , - 93 , - 92 , - 91 , - 89 , - 88 , - 86 , - 85 , - 84 , - 82 , - 81 , - 79 , - 78 , - 77 , - 75 , - 74 , - 72 , - 71 , - 70 , - 68 , - 67 , - 65 , - 64 , - 63 , - 61 , - 60 , - 58 , - 57 , - 56 , - 54 , - 53 , - 51 , - 50 , - 49 , - 47 , - 46 , - 44 , - 43 , - 42 , - 40 , - 39 , - 37 , - 36 , - 35 , - 33 , - 32 , - 30 , - 29 , - 28 , - 26 , - 25 , - 23 , - 22 , - 21 , - 19 , - 18 , - 16 , - 15 , - 14 , - 12 , - 11 , - 9 , - 8 , - 7 , - 5 , - 4 , - 2 , - 1 , 0 , 1 , 2 , 4 , 5 , 7 , 8 , 9 , 11 , 12 , 14 , 15 , 16 , 18 , 19 , 21 , 22 , 23 , 25 , 26 , 28 , 29 , 30 , 32 , 33 , 35 , 36 , 37 , 39 , 40 , 42 , 43 , 44 , 46 , 47 , 49 , 50 , 51 , 53 , 54 , 56 , 57 , 58 , 60 , 61 , 63 , 64 , 65 , 67 , 68 , 70 , 71 , 72 , 74 , 75 , 77 , 78 , 79 , 81 , 82 , 84 , 85 , 86 , 88 , 89 , 91 , 92 , 93 , 95 , 96 , 98 , 99 , 100 , 102 , 103 , 105 , 106 , 107 , 109 , 110 , 112 , 113 , 114 , 116 , 117 , 119 , 120 , 121 , 123 , 124 , 126 , 127 , 128 , 130 , 131 , 133 , 134 , 135 , 137 , 138 , 140 , 141 , 143 , 144 , 145 , 147 , 148 , 150 , 151 , 152 , 154 , 155 , 157 , 158 , 159 , 161 , 162 , 164 , 165 , 166 , 168 , 169 , 171 , 172 , 173 , 175 , 176 } ;
static short g_u_table_global [ ] = { - 44 , - 43 , - 43 , - 43 , - 42 , - 42 , - 41 , - 41 , - 41 , - 40 , - 40 , - 40 , - 39 , - 39 , - 39 , - 38 , - 38 , - 38 , - 37 , - 37 , - 37 , - 36 , - 36 , - 36 , - 35 , - 35 , - 35 , - 34 , - 34 , - 34 , - 33 , - 33 , - 33 , - 32 , - 32 , - 31 , - 31 , - 31 , - 30 , - 30 , - 30 , - 29 , - 29 , - 29 , - 28 , - 28 , - 28 , - 27 , - 27 , - 27 , - 26 , - 26 , - 26 , - 25 , - 25 , - 25 , - 24 , - 24 , - 24 , - 23 , - 23 , - 23 , - 22 , - 22 , - 22 , - 21 , - 21 , - 20 , - 20 , - 20 , - 19 , - 19 , - 19 , - 18 , - 18 , - 18 , - 17 , - 17 , - 17 , - 16 , - 16 , - 16 , - 15 , - 15 , - 15 , - 14 , - 14 , - 14 , - 13 , - 13 , - 13 , - 12 , - 12 , - 12 , - 11 , - 11 , - 11 , - 10 , - 10 , - 9 , - 9 , - 9 , - 8 , - 8 , - 8 , - 7 , - 7 , - 7 , - 6 , - 6 , - 6 , - 5 , - 5 , - 5 , - 4 , - 4 , - 4 , - 3 , - 3 , - 3 , - 2 , - 2 , - 2 , - 1 , - 1 , - 1 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 2 , 2 , 2 , 3 , 3 , 3 , 4 , 4 , 4 , 5 , 5 , 5 , 6 , 6 , 6 , 7 , 7 , 7 , 8 , 8 , 8 , 9 , 9 , 9 , 10 , 10 , 11 , 11 , 11 , 12 , 12 , 12 , 13 , 13 , 13 , 14 , 14 , 14 , 15 , 15 , 15 , 16 , 16 , 16 , 17 , 17 , 17 , 18 , 18 , 18 , 19 , 19 , 19 , 20 , 20 , 20 , 21 , 21 , 22 , 22 , 22 , 23 , 23 , 23 , 24 , 24 , 24 , 25 , 25 , 25 , 26 , 26 , 26 , 27 , 27 , 27 , 28 , 28 , 28 , 29 , 29 , 29 , 30 , 30 , 30 , 31 , 31 , 31 , 32 , 32 , 33 , 33 , 33 , 34 , 34 , 34 , 35 , 35 , 35 , 36 , 36 , 36 , 37 , 37 , 37 , 38 , 38 , 38 , 39 , 39 , 39 , 40 , 40 , 40 , 41 , 41 , 41 , 42 , 42 , 43 , 43 } ;
static short g_v_table_global [ ] = { - 91 , - 90 , - 89 , - 89 , - 88 , - 87 , - 87 , - 86 , - 85 , - 84 , - 84 , - 83 , - 82 , - 82 , - 81 , - 80 , - 79 , - 79 , - 78 , - 77 , - 77 , - 76 , - 75 , - 74 , - 74 , - 73 , - 72 , - 72 , - 71 , - 70 , - 69 , - 69 , - 68 , - 67 , - 67 , - 66 , - 65 , - 64 , - 64 , - 63 , - 62 , - 62 , - 61 , - 60 , - 59 , - 59 , - 58 , - 57 , - 57 , - 56 , - 55 , - 54 , - 54 , - 53 , - 52 , - 52 , - 51 , - 50 , - 49 , - 49 , - 48 , - 47 , - 47 , - 46 , - 45 , - 44 , - 44 , - 43 , - 42 , - 42 , - 41 , - 40 , - 39 , - 39 , - 38 , - 37 , - 37 , - 36 , - 35 , - 34 , - 34 , - 33 , - 32 , - 32 , - 31 , - 30 , - 29 , - 29 , - 28 , - 27 , - 27 , - 26 , - 25 , - 24 , - 24 , - 23 , - 22 , - 22 , - 21 , - 20 , - 19 , - 19 , - 18 , - 17 , - 17 , - 16 , - 15 , - 14 , - 14 , - 13 , - 12 , - 12 , - 11 , - 10 , - 9 , - 9 , - 8 , - 7 , - 7 , - 6 , - 5 , - 4 , - 4 , - 3 , - 2 , - 2 , - 1 , 0 , 0 , 0 , 1 , 2 , 2 , 3 , 4 , 4 , 5 , 6 , 7 , 7 , 8 , 9 , 9 , 10 , 11 , 12 , 12 , 13 , 14 , 14 , 15 , 16 , 17 , 17 , 18 , 19 , 19 , 20 , 21 , 22 , 22 , 23 , 24 , 24 , 25 , 26 , 27 , 27 , 28 , 29 , 29 , 30 , 31 , 32 , 32 , 33 , 34 , 34 , 35 , 36 , 37 , 37 , 38 , 39 , 39 , 40 , 41 , 42 , 42 , 43 , 44 , 44 , 45 , 46 , 47 , 47 , 48 , 49 , 49 , 50 , 51 , 52 , 52 , 53 , 54 , 54 , 55 , 56 , 57 , 57 , 58 , 59 , 59 , 60 , 61 , 62 , 62 , 63 , 64 , 64 , 65 , 66 , 67 , 67 , 68 , 69 , 69 , 70 , 71 , 72 , 72 , 73 , 74 , 74 , 75 , 76 , 77 , 77 , 78 , 79 , 79 , 80 , 81 , 82 , 82 , 83 , 84 , 84 , 85 , 86 , 87 , 87 , 88 , 89 , 89 } ;
static short b_u_table_global [ ] = { - 226 , - 225 , - 223 , - 221 , - 219 , - 217 , - 216 , - 214 , - 212 , - 210 , - 209 , - 207 , - 205 , - 203 , - 202 , - 200 , - 198 , - 196 , - 194 , - 193 , - 191 , - 189 , - 187 , - 186 , - 184 , - 182 , - 180 , - 178 , - 177 , - 175 , - 173 , - 171 , - 170 , - 168 , - 166 , - 164 , - 163 , - 161 , - 159 , - 157 , - 155 , - 154 , - 152 , - 150 , - 148 , - 147 , - 145 , - 143 , - 141 , - 139 , - 138 , - 136 , - 134 , - 132 , - 131 , - 129 , - 127 , - 125 , - 124 , - 122 , - 120 , - 118 , - 116 , - 115 , - 113 , - 111 , - 109 , - 108 , - 106 , - 104 , - 102 , - 101 , - 99 , - 97 , - 95 , - 93 , - 92 , - 90 , - 88 , - 86 , - 85 , - 83 , - 81 , - 79 , - 77 , - 76 , - 74 , - 72 , - 70 , - 69 , - 67 , - 65 , - 63 , - 62 , - 60 , - 58 , - 56 , - 54 , - 53 , - 51 , - 49 , - 47 , - 46 , - 44 , - 42 , - 40 , - 38 , - 37 , - 35 , - 33 , - 31 , - 30 , - 28 , - 26 , - 24 , - 23 , - 21 , - 19 , - 17 , - 15 , - 14 , - 12 , - 10 , - 8 , - 7 , - 5 , - 3 , - 1 , 0 , 1 , 3 , 5 , 7 , 8 , 10 , 12 , 14 , 15 , 17 , 19 , 21 , 23 , 24 , 26 , 28 , 30 , 31 , 33 , 35 , 37 , 38 , 40 , 42 , 44 , 46 , 47 , 49 , 51 , 53 , 54 , 56 , 58 , 60 , 62 , 63 , 65 , 67 , 69 , 70 , 72 , 74 , 76 , 77 , 79 , 81 , 83 , 85 , 86 , 88 , 90 , 92 , 93 , 95 , 97 , 99 , 101 , 102 , 104 , 106 , 108 , 109 , 111 , 113 , 115 , 116 , 118 , 120 , 122 , 124 , 125 , 127 , 129 , 131 , 132 , 134 , 136 , 138 , 139 , 141 , 143 , 145 , 147 , 148 , 150 , 152 , 154 , 155 , 157 , 159 , 161 , 163 , 164 , 166 , 168 , 170 , 171 , 173 , 175 , 177 , 178 , 180 , 182 , 184 , 186 , 187 , 189 , 191 , 193 , 194 , 196 , 198 , 200 , 202 , 203 , 205 , 207 , 209 , 210 , 212 , 214 , 216 , 217 , 219 , 221 , 223 } ;
2013-03-17 07:45:21 +08:00
bool Image : : initialised = false ;
static unsigned char * y_table ;
static signed char * uv_table ;
static short * r_v_table ;
static short * g_v_table ;
static short * g_u_table ;
static short * b_u_table ;
2020-08-26 07:45:48 +08:00
struct SwsContext * sws_convert_context = nullptr ;
2020-04-27 06:19:30 +08:00
2020-08-26 07:45:48 +08:00
jpeg_compress_struct * Image : : writejpg_ccinfo [ 101 ] = { } ;
jpeg_compress_struct * Image : : encodejpg_ccinfo [ 101 ] = { } ;
jpeg_decompress_struct * Image : : readjpg_dcinfo = nullptr ;
jpeg_decompress_struct * Image : : decodejpg_dcinfo = nullptr ;
2013-03-17 07:45:21 +08:00
struct zm_error_mgr Image : : jpg_err ;
/* Pointer to blend function. */
static blend_fptr_t fptr_blend ;
/* Pointer to delta8 functions */
static delta_fptr_t fptr_delta8_rgb ;
static delta_fptr_t fptr_delta8_bgr ;
static delta_fptr_t fptr_delta8_rgba ;
static delta_fptr_t fptr_delta8_bgra ;
static delta_fptr_t fptr_delta8_argb ;
static delta_fptr_t fptr_delta8_abgr ;
static delta_fptr_t fptr_delta8_gray8 ;
/* Pointers to deinterlace_4field functions */
static deinterlace_4field_fptr_t fptr_deinterlace_4field_rgba ;
static deinterlace_4field_fptr_t fptr_deinterlace_4field_bgra ;
static deinterlace_4field_fptr_t fptr_deinterlace_4field_argb ;
static deinterlace_4field_fptr_t fptr_deinterlace_4field_abgr ;
static deinterlace_4field_fptr_t fptr_deinterlace_4field_gray8 ;
/* Pointer to image buffer memory copy function */
imgbufcpy_fptr_t fptr_imgbufcpy ;
2020-11-28 21:16:20 +08:00
/* Font */
static ZmFont font ;
2019-02-22 22:44:57 +08:00
void Image : : update_function_pointers ( ) {
/* Because many loops are unrolled and work on 16 colours/time or 4 pixels/time, we have to meet requirements */
if ( pixels % 16 | | pixels % 12 ) {
// have to use non-loop unrolled functions
delta8_rgb = & std_delta8_rgb ;
delta8_bgr = & std_delta8_bgr ;
delta8_rgba = & std_delta8_rgba ;
delta8_bgra = & std_delta8_bgra ;
delta8_argb = & std_delta8_argb ;
delta8_abgr = & std_delta8_abgr ;
delta8_gray8 = & std_delta8_gray8 ;
blend = & std_blend ;
} else {
// Use either sse or neon, or loop unrolled version
delta8_rgb = fptr_delta8_rgb ;
delta8_bgr = fptr_delta8_bgr ;
delta8_rgba = fptr_delta8_rgba ;
delta8_bgra = fptr_delta8_bgra ;
delta8_argb = fptr_delta8_argb ;
delta8_abgr = fptr_delta8_abgr ;
delta8_gray8 = fptr_delta8_gray8 ;
blend = fptr_blend ;
}
}
// This constructor is not used anywhere
2017-04-28 22:34:48 +08:00
Image : : Image ( ) {
2016-04-04 22:11:48 +08:00
if ( ! initialised )
Initialise ( ) ;
width = 0 ;
2020-07-08 04:13:41 +08:00
linesize = 0 ;
2016-04-04 22:11:48 +08:00
height = 0 ;
pixels = 0 ;
colours = 0 ;
subpixelorder = 0 ;
size = 0 ;
allocation = 0 ;
buffer = 0 ;
2020-11-20 05:37:28 +08:00
buffertype = ZM_BUFTYPE_DONTFREE ;
2016-04-04 22:11:48 +08:00
holdbuffer = 0 ;
text [ 0 ] = ' \0 ' ;
2019-02-22 22:44:57 +08:00
blend = fptr_blend ;
2013-03-17 07:45:21 +08:00
}
2020-07-23 05:30:28 +08:00
Image : : Image ( const char * filename ) {
2016-04-04 22:11:48 +08:00
if ( ! initialised )
Initialise ( ) ;
width = 0 ;
2020-07-08 04:13:41 +08:00
linesize = 0 ;
2016-04-04 22:11:48 +08:00
height = 0 ;
pixels = 0 ;
colours = 0 ;
2019-09-17 22:31:48 +08:00
subpixelorder = 0 ;
2016-04-04 22:11:48 +08:00
size = 0 ;
allocation = 0 ;
buffer = 0 ;
2020-11-20 05:37:28 +08:00
buffertype = ZM_BUFTYPE_DONTFREE ;
2016-04-04 22:11:48 +08:00
holdbuffer = 0 ;
2019-02-22 22:44:57 +08:00
ReadJpeg ( filename , ZM_COLOUR_RGB24 , ZM_SUBPIX_ORDER_RGB ) ;
2016-04-04 22:11:48 +08:00
text [ 0 ] = ' \0 ' ;
2019-02-22 22:44:57 +08:00
update_function_pointers ( ) ;
2013-03-17 07:45:21 +08:00
}
2020-11-20 05:37:28 +08:00
Image : : Image ( int p_width , int p_height , int p_colours , int p_subpixelorder , uint8_t * p_buffer , unsigned int p_padding ) :
width ( p_width ) ,
height ( p_height ) ,
colours ( p_colours ) ,
padding ( p_padding ) ,
subpixelorder ( p_subpixelorder ) ,
buffer ( p_buffer ) {
2016-04-04 22:11:48 +08:00
if ( ! initialised )
Initialise ( ) ;
2020-11-20 05:37:28 +08:00
pixels = width * height ;
2020-07-08 04:13:41 +08:00
linesize = p_width * p_colours ;
2020-11-20 05:37:28 +08:00
size = linesize * height + padding ;
2020-08-26 07:45:48 +08:00
buffer = nullptr ;
2020-07-08 04:13:41 +08:00
holdbuffer = 0 ;
if ( p_buffer ) {
allocation = size ;
buffertype = ZM_BUFTYPE_DONTFREE ;
buffer = p_buffer ;
} else {
AllocImgBuffer ( size ) ;
}
text [ 0 ] = ' \0 ' ;
2021-01-12 00:57:34 +08:00
imagePixFormat = AVPixFormat ( ) ;
2020-07-08 04:13:41 +08:00
update_function_pointers ( ) ;
}
2020-11-20 05:37:28 +08:00
Image : : Image ( int p_width , int p_linesize , int p_height , int p_colours , int p_subpixelorder , uint8_t * p_buffer , unsigned int p_padding ) :
width ( p_width ) ,
linesize ( p_linesize ) ,
height ( p_height ) ,
colours ( p_colours ) ,
padding ( p_padding ) ,
subpixelorder ( p_subpixelorder ) ,
buffer ( p_buffer )
{
2020-07-08 04:13:41 +08:00
if ( ! initialised )
Initialise ( ) ;
pixels = width * height ;
size = linesize * height + padding ;
2020-08-26 07:45:48 +08:00
buffer = nullptr ;
2016-04-04 22:11:48 +08:00
holdbuffer = 0 ;
2017-08-11 03:44:20 +08:00
if ( p_buffer ) {
2016-04-04 22:11:48 +08:00
allocation = size ;
buffertype = ZM_BUFTYPE_DONTFREE ;
buffer = p_buffer ;
2017-08-11 03:44:20 +08:00
} else {
2016-04-04 22:11:48 +08:00
AllocImgBuffer ( size ) ;
}
text [ 0 ] = ' \0 ' ;
2021-01-12 00:57:34 +08:00
imagePixFormat = AVPixFormat ( ) ;
2019-02-22 22:44:57 +08:00
update_function_pointers ( ) ;
2013-03-17 07:45:21 +08:00
}
2020-04-27 06:19:30 +08:00
Image : : Image ( const AVFrame * frame ) {
2017-11-19 05:00:10 +08:00
text [ 0 ] = ' \0 ' ;
2017-08-24 03:05:44 +08:00
width = frame - > width ;
height = frame - > height ;
pixels = width * height ;
2020-02-21 22:34:44 +08:00
2021-01-12 00:57:34 +08:00
zm_dump_video_frame ( frame , " Image.Assign(frame) " ) ;
2020-07-23 01:41:27 +08:00
// FIXME
2017-08-24 03:05:44 +08:00
colours = ZM_COLOUR_RGB32 ;
subpixelorder = ZM_SUBPIX_ORDER_RGBA ;
2021-01-27 01:22:07 +08:00
imagePixFormat = AV_PIX_FMT_RGBA ;
//(AVPixelFormat)frame->format;
2020-02-21 22:34:44 +08:00
2021-01-12 00:57:34 +08:00
# if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
2021-01-27 01:22:07 +08:00
size = av_image_get_buffer_size ( AV_PIX_FMT_RGBA , width , height , 32 ) ;
2020-07-25 03:26:28 +08:00
// av_image_get_linesize isn't aligned, so we have to do that.
2021-01-27 01:22:07 +08:00
linesize = FFALIGN ( av_image_get_linesize ( AV_PIX_FMT_RGBA , width , 0 ) , 32 ) ;
2020-07-25 03:26:28 +08:00
# else
2021-01-27 01:22:07 +08:00
linesize = FFALIGN ( av_image_get_linesize ( AV_PIX_FMT_RGBA , width , 0 ) , 1 ) ;
2021-01-12 00:57:34 +08:00
size = avpicture_get_size ( AV_PIX_FMT_RGB0 , width , height ) ;
2020-07-25 03:26:28 +08:00
# endif
2020-08-26 07:45:48 +08:00
buffer = nullptr ;
2017-08-24 03:05:44 +08:00
holdbuffer = 0 ;
AllocImgBuffer ( size ) ;
2020-07-23 01:41:27 +08:00
this - > Assign ( frame ) ;
2017-11-10 03:50:20 +08:00
}
2017-08-24 03:05:44 +08:00
2021-01-12 02:35:23 +08:00
static void dont_free ( void * opaque , uint8_t * data ) {
}
2021-01-12 00:57:34 +08:00
int Image : : PopulateFrame ( AVFrame * frame ) {
Debug ( 1 , " PopulateFrame: width %d height %d linesize %d colours %d imagesize %d %s " ,
width , height , linesize , colours , size ,
av_get_pix_fmt_name ( imagePixFormat )
) ;
AVBufferRef * ref = av_buffer_create ( buffer , size ,
2021-01-12 02:35:23 +08:00
dont_free , /* Free callback */
2021-01-12 00:57:34 +08:00
nullptr , /* opaque */
0 /* flags */
) ;
2021-04-01 23:51:18 +08:00
if ( ! ref ) {
2021-01-31 09:34:39 +08:00
Warning ( " Failed to create av_buffer " ) ;
2021-01-12 00:57:34 +08:00
}
2021-01-12 02:35:23 +08:00
frame - > buf [ 0 ] = ref ;
2021-01-12 00:57:34 +08:00
# if LIBAVUTIL_VERSION_CHECK(54, 6, 0, 6, 0)
// From what I've read, we should align the linesizes to 32bit so that ffmpeg can use SIMD instructions too.
int size = av_image_fill_arrays (
frame - > data , frame - > linesize ,
buffer , imagePixFormat , width , height ,
32 //alignment
) ;
if ( size < 0 ) {
Error ( " Problem setting up data pointers into image %s " ,
av_make_error_string ( size ) . c_str ( ) ) ;
return size ;
}
# else
avpicture_fill ( ( AVPicture * ) frame , buffer ,
imagePixFormat , width , height ) ;
# endif
2021-01-12 02:35:23 +08:00
frame - > width = width ;
frame - > height = height ;
frame - > format = imagePixFormat ;
2021-01-12 00:57:34 +08:00
Debug ( 1 , " PopulateFrame: width %d height %d linesize %d colours %d imagesize %d " , width , height , linesize , colours , size ) ;
zm_dump_video_frame ( frame , " Image.Populate(frame) " ) ;
return 1 ;
2021-04-01 23:51:18 +08:00
} // int Image::PopulateFrame(AVFrame *frame)
2021-01-12 00:57:34 +08:00
2020-07-23 01:41:27 +08:00
void Image : : Assign ( const AVFrame * frame ) {
2020-12-24 07:18:16 +08:00
/* Assume the dimensions etc are correct. FIXME */
2017-11-10 03:50:20 +08:00
2021-01-12 00:57:34 +08:00
// Desired format
2017-11-18 03:30:38 +08:00
AVPixelFormat format = ( AVPixelFormat ) AVPixFormat ( ) ;
2017-11-15 05:16:07 +08:00
2017-11-10 03:50:20 +08:00
AVFrame * dest_frame = zm_av_frame_alloc ( ) ;
2021-01-12 00:57:34 +08:00
PopulateFrame ( dest_frame ) ;
2021-01-31 09:34:39 +08:00
zm_dump_video_frame ( frame , " source frame before convert " ) ;
2021-02-20 01:43:53 +08:00
dest_frame - > pts = frame - > pts ;
2017-08-24 03:05:44 +08:00
# if HAVE_LIBSWSCALE
2020-04-27 06:19:30 +08:00
sws_convert_context = sws_getCachedContext (
sws_convert_context ,
2021-02-13 00:55:25 +08:00
frame - > width , frame - > height , ( AVPixelFormat ) frame - > format ,
2021-01-12 00:57:34 +08:00
width , height , format ,
2021-02-20 02:44:37 +08:00
//SWS_BICUBIC,
SWS_POINT | SWS_BITEXACT ,
nullptr , nullptr , nullptr ) ;
2020-08-26 07:45:48 +08:00
if ( sws_convert_context = = nullptr )
2020-04-27 06:19:30 +08:00
Fatal ( " Unable to create conversion context " ) ;
2017-08-24 03:05:44 +08:00
2021-01-12 00:57:34 +08:00
if ( sws_scale ( sws_convert_context ,
frame - > data , frame - > linesize , 0 , frame - > height ,
2020-04-27 06:19:30 +08:00
dest_frame - > data , dest_frame - > linesize ) < 0 )
2021-02-10 09:29:37 +08:00
Fatal ( " Unable to convert raw format %u %ux%u to target format %u %ux%u " ,
frame - > format , frame - > width , frame - > height ,
format , width , height ) ;
2017-08-24 03:05:44 +08:00
# else // HAVE_LIBSWSCALE
Fatal ( " You must compile ffmpeg with the --enable-swscale option to use ffmpeg cameras " ) ;
# endif // HAVE_LIBSWSCALE
2021-01-12 00:57:34 +08:00
zm_dump_video_frame ( dest_frame , " dest frame after convert " ) ;
2020-04-27 06:19:30 +08:00
av_frame_free ( & dest_frame ) ;
2019-02-22 22:44:57 +08:00
update_function_pointers ( ) ;
2021-01-31 09:34:39 +08:00
} // end Image::Assign(const AVFrame *frame)
2017-08-24 03:05:44 +08:00
2020-04-27 06:19:30 +08:00
Image : : Image ( const Image & p_image ) {
2016-04-04 22:11:48 +08:00
if ( ! initialised )
Initialise ( ) ;
width = p_image . width ;
2020-07-08 04:13:41 +08:00
linesize = p_image . linesize ;
2016-04-04 22:11:48 +08:00
height = p_image . height ;
pixels = p_image . pixels ;
colours = p_image . colours ;
subpixelorder = p_image . subpixelorder ;
size = p_image . size ; // allocation is set in AllocImgBuffer
2020-08-26 07:45:48 +08:00
buffer = nullptr ;
2016-04-04 22:11:48 +08:00
holdbuffer = 0 ;
AllocImgBuffer ( size ) ;
( * fptr_imgbufcpy ) ( buffer , p_image . buffer , size ) ;
2020-04-27 06:19:30 +08:00
strncpy ( text , p_image . text , sizeof ( text ) ) ;
2021-01-12 00:57:34 +08:00
imagePixFormat = p_image . imagePixFormat ;
2019-02-22 22:44:57 +08:00
update_function_pointers ( ) ;
2013-03-17 07:45:21 +08:00
}
2016-10-07 23:35:14 +08:00
Image : : ~ Image ( ) {
2016-04-04 22:11:48 +08:00
DumpImgBuffer ( ) ;
2016-10-07 23:35:14 +08:00
}
/* Should be called as part of program shutdown to free everything */
void Image : : Deinitialise ( ) {
2020-04-27 06:19:30 +08:00
if ( ! initialised ) return ;
initialised = false ;
if ( readjpg_dcinfo ) {
2020-07-21 09:02:06 +08:00
jpeg_destroy_decompress ( readjpg_dcinfo ) ;
2020-04-27 06:19:30 +08:00
delete readjpg_dcinfo ;
2020-08-26 07:45:48 +08:00
readjpg_dcinfo = nullptr ;
2020-04-27 06:19:30 +08:00
}
if ( decodejpg_dcinfo ) {
2020-07-21 09:02:06 +08:00
jpeg_destroy_decompress ( decodejpg_dcinfo ) ;
2020-04-27 06:19:30 +08:00
delete decodejpg_dcinfo ;
2020-08-26 07:45:48 +08:00
decodejpg_dcinfo = nullptr ;
2020-04-27 06:19:30 +08:00
}
for ( unsigned int quality = 0 ; quality < = 100 ; quality + = 1 ) {
if ( writejpg_ccinfo [ quality ] ) {
2020-07-21 09:02:06 +08:00
jpeg_destroy_compress ( writejpg_ccinfo [ quality ] ) ;
2020-04-27 06:19:30 +08:00
delete writejpg_ccinfo [ quality ] ;
2020-08-26 07:45:48 +08:00
writejpg_ccinfo [ quality ] = nullptr ;
2016-10-07 23:35:14 +08:00
}
2020-04-27 06:19:30 +08:00
} // end foreach quality
if ( sws_convert_context ) {
sws_freeContext ( sws_convert_context ) ;
2020-08-26 07:45:48 +08:00
sws_convert_context = nullptr ;
2016-04-04 22:11:48 +08:00
}
2020-04-27 06:19:30 +08:00
} // end void Image::Deinitialise()
2013-03-17 07:45:21 +08:00
2017-08-11 03:44:20 +08:00
void Image : : Initialise ( ) {
2016-04-04 22:11:48 +08:00
/* Assign the blend pointer to function */
2017-08-11 03:44:20 +08:00
if ( config . fast_image_blends ) {
2020-07-20 05:50:47 +08:00
if ( config . cpu_extensions & & sse_version > = 20 ) {
2016-04-04 22:11:48 +08:00
fptr_blend = & sse2_fastblend ; /* SSE2 fast blend */
2020-07-08 04:13:41 +08:00
Debug ( 4 , " Blend: Using SSE2 fast blend function " ) ;
2017-08-11 03:44:20 +08:00
} else if ( config . cpu_extensions & & neonversion > = 1 ) {
2017-05-13 03:59:08 +08:00
# if defined(__aarch64__)
fptr_blend = & neon64_armv8_fastblend ; /* ARM Neon (AArch64) fast blend */
2020-07-08 04:13:41 +08:00
Debug ( 4 , " Blend: Using ARM Neon (AArch64) fast blend function " ) ;
2017-05-13 03:59:08 +08:00
# elif defined(__arm__)
fptr_blend = & neon32_armv7_fastblend ; /* ARM Neon (AArch32) fast blend */
2020-07-08 04:13:41 +08:00
Debug ( 4 , " Blend: Using ARM Neon (AArch32) fast blend function " ) ;
2017-05-13 03:59:08 +08:00
# else
Panic ( " Bug: Non ARM platform but neon present " ) ;
# endif
2016-04-04 22:11:48 +08:00
} else {
fptr_blend = & std_fastblend ; /* standard fast blend */
2020-07-08 04:13:41 +08:00
Debug ( 4 , " Blend: Using fast blend function " ) ;
2016-04-04 22:11:48 +08:00
}
} else {
fptr_blend = & std_blend ;
2020-07-08 04:13:41 +08:00
Debug ( 4 , " Blend: Using standard blend function " ) ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
2017-04-16 15:57:37 +08:00
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t blend1 [ 128 ] = {
86 , 58 , 54 , 63 , 149 , 62 , 209 , 34 , 148 , 46 , 186 , 176 , 9 , 236 , 193 , 254 , 113 , 146 , 228 , 220 , 123 , 164 , 92 , 98 , 9 , 72 , 67 , 156 , 63 , 118 , 96 , 167 ,
48 , 224 , 106 , 176 , 201 , 245 , 223 , 219 , 198 , 50 , 100 , 31 , 68 , 77 , 33 , 76 , 166 , 90 , 254 , 128 , 191 , 82 , 84 , 32 , 3 , 171 , 147 , 248 , 14 , 196 , 141 , 179 ,
79 , 237 , 121 , 11 , 132 , 37 , 194 , 225 , 45 , 171 , 169 , 167 , 56 , 64 , 193 , 85 , 147 , 33 , 97 , 221 , 94 , 97 , 90 , 44 , 191 , 248 , 65 , 8 , 17 , 240 , 167 , 207 ,
224 , 23 , 71 , 74 , 81 , 1 , 46 , 110 , 227 , 94 , 163 , 170 , 55 , 155 , 52 , 147 , 224 , 154 , 237 , 35 , 255 , 26 , 229 , 11 , 223 , 242 , 118 , 155 , 82 , 37 , 189 , 2
} ;
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t blend2 [ 128 ] = {
92 , 188 , 203 , 118 , 121 , 231 , 252 , 218 , 126 , 88 , 80 , 72 , 123 , 16 , 91 , 131 , 109 , 0 , 57 , 56 , 95 , 204 , 74 , 8 , 137 , 94 , 6 , 69 , 18 , 146 , 229 , 194 ,
146 , 230 , 13 , 146 , 95 , 48 , 185 , 65 , 162 , 47 , 152 , 172 , 184 , 111 , 245 , 143 , 247 , 105 , 49 , 42 , 89 , 37 , 145 , 255 , 221 , 200 , 103 , 80 , 98 , 39 , 14 , 227 ,
227 , 46 , 46 , 59 , 248 , 7 , 83 , 20 , 157 , 79 , 36 , 161 , 237 , 55 , 77 , 175 , 232 , 200 , 38 , 170 , 198 , 239 , 89 , 19 , 82 , 88 , 130 , 120 , 203 , 184 , 141 , 117 ,
228 , 140 , 150 , 107 , 103 , 195 , 74 , 130 , 42 , 11 , 150 , 70 , 176 , 204 , 198 , 188 , 38 , 252 , 174 , 104 , 128 , 106 , 31 , 17 , 141 , 231 , 62 , 104 , 179 , 29 , 143 , 130
} ;
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t blendexp [ 128 ] = {
86 , 73 , 71 , 69 , 145 , 82 , 214 , 56 , 145 , 51 , 173 , 163 , 22 , 209 , 180 , 239 , 112 , 128 , 207 , 200 , 119 , 168 , 89 , 87 , 24 , 74 , 59 , 145 , 57 , 121 , 111 , 170 ,
59 , 224 , 94 , 172 , 188 , 221 , 218 , 200 , 193 , 49 , 106 , 47 , 81 , 81 , 58 , 84 , 175 , 91 , 229 , 117 , 178 , 76 , 91 , 58 , 29 , 174 , 141 , 227 , 24 , 177 , 125 , 184 ,
96 , 214 , 112 , 16 , 145 , 33 , 180 , 200 , 58 , 159 , 153 , 166 , 77 , 62 , 179 , 95 , 157 , 53 , 89 , 214 , 106 , 114 , 89 , 41 , 177 , 228 , 72 , 21 , 39 , 233 , 163 , 196 ,
224 , 37 , 80 , 77 , 83 , 24 , 49 , 112 , 204 , 84 , 161 , 158 , 69 , 160 , 69 , 151 , 201 , 165 , 229 , 43 , 239 , 35 , 205 , 11 , 213 , 240 , 111 , 148 , 93 , 36 , 183 , 17
} ;
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t blendres [ 128 ] ;
/* Run the blend function */
( * fptr_blend ) ( blend1 , blend2 , blendres , 128 , 12.0 ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Compare results with expected results */
2020-07-23 05:30:28 +08:00
for ( int i = 0 ; i < 128 ; i + + ) {
2017-08-11 03:44:20 +08:00
if ( abs ( blendexp [ i ] - blendres [ i ] ) > 3 ) {
2017-05-11 19:13:04 +08:00
Panic ( " Blend function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u " , i , blendexp [ i ] , blendres [ i ] ) ;
2016-04-04 22:11:48 +08:00
}
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
fptr_delta8_rgb = & std_delta8_rgb ;
fptr_delta8_bgr = & std_delta8_bgr ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Assign the delta functions */
2017-08-11 03:44:20 +08:00
if ( config . cpu_extensions ) {
2020-07-20 05:50:47 +08:00
if ( sse_version > = 35 ) {
2016-04-04 22:11:48 +08:00
/* SSSE3 available */
fptr_delta8_rgba = & ssse3_delta8_rgba ;
fptr_delta8_bgra = & ssse3_delta8_bgra ;
fptr_delta8_argb = & ssse3_delta8_argb ;
fptr_delta8_abgr = & ssse3_delta8_abgr ;
fptr_delta8_gray8 = & sse2_delta8_gray8 ;
2020-07-20 05:50:47 +08:00
Debug ( 4 , " Delta: Using SSSE3 delta functions " ) ;
} else if ( sse_version > = 20 ) {
2016-04-04 22:11:48 +08:00
/* SSE2 available */
fptr_delta8_rgba = & sse2_delta8_rgba ;
fptr_delta8_bgra = & sse2_delta8_bgra ;
fptr_delta8_argb = & sse2_delta8_argb ;
fptr_delta8_abgr = & sse2_delta8_abgr ;
fptr_delta8_gray8 = & sse2_delta8_gray8 ;
2020-07-20 05:50:47 +08:00
Debug ( 4 , " Delta: Using SSE2 delta functions " ) ;
2017-08-11 03:44:20 +08:00
} else if ( neonversion > = 1 ) {
2017-03-19 19:02:42 +08:00
/* ARM Neon available */
2017-05-13 14:19:13 +08:00
# if defined(__aarch64__)
fptr_delta8_rgba = & neon64_armv8_delta8_rgba ;
fptr_delta8_bgra = & neon64_armv8_delta8_bgra ;
fptr_delta8_argb = & neon64_armv8_delta8_argb ;
fptr_delta8_abgr = & neon64_armv8_delta8_abgr ;
fptr_delta8_gray8 = & neon64_armv8_delta8_gray8 ;
2020-07-20 05:50:47 +08:00
Debug ( 4 , " Delta: Using ARM Neon (AArch64) delta functions " ) ;
2017-05-13 14:19:13 +08:00
# elif defined(__arm__)
2017-03-19 19:02:42 +08:00
fptr_delta8_rgba = & neon32_armv7_delta8_rgba ;
fptr_delta8_bgra = & neon32_armv7_delta8_bgra ;
fptr_delta8_argb = & neon32_armv7_delta8_argb ;
fptr_delta8_abgr = & neon32_armv7_delta8_abgr ;
fptr_delta8_gray8 = & neon32_armv7_delta8_gray8 ;
2020-07-20 05:50:47 +08:00
Debug ( 4 , " Delta: Using ARM Neon (AArch32) delta functions " ) ;
2017-05-13 14:19:13 +08:00
# else
Panic ( " Bug: Non ARM platform but neon present " ) ;
# endif
2016-04-04 22:11:48 +08:00
} else {
/* No suitable SSE version available */
2019-02-22 22:44:57 +08:00
fptr_delta8_rgba = & fast_delta8_rgba ;
fptr_delta8_bgra = & fast_delta8_bgra ;
fptr_delta8_argb = & fast_delta8_argb ;
fptr_delta8_abgr = & fast_delta8_abgr ;
fptr_delta8_gray8 = & fast_delta8_gray8 ;
2016-10-07 23:35:14 +08:00
Debug ( 4 , " Delta: Using standard delta functions " ) ;
2016-04-04 22:11:48 +08:00
}
} else {
/* CPU extensions disabled */
2019-02-22 22:44:57 +08:00
fptr_delta8_rgba = & fast_delta8_rgba ;
fptr_delta8_bgra = & fast_delta8_bgra ;
fptr_delta8_argb = & fast_delta8_argb ;
fptr_delta8_abgr = & fast_delta8_abgr ;
fptr_delta8_gray8 = & fast_delta8_gray8 ;
2016-10-07 23:35:14 +08:00
Debug ( 4 , " Delta: CPU extensions disabled, using standard delta functions " ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2017-04-16 15:57:37 +08:00
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t delta8_1 [ 128 ] = {
221 , 22 , 234 , 254 , 8 , 140 , 15 , 28 , 166 , 13 , 203 , 56 , 92 , 250 , 79 , 225 , 19 , 59 , 241 , 145 , 253 , 33 , 87 , 204 , 97 , 168 , 229 , 180 , 3 , 108 , 205 , 177 ,
41 , 108 , 65 , 149 , 4 , 87 , 16 , 240 , 56 , 50 , 135 , 64 , 153 , 3 , 219 , 214 , 239 , 55 , 169 , 180 , 167 , 45 , 243 , 56 , 191 , 119 , 145 , 250 , 102 , 145 , 73 , 32 ,
207 , 213 , 189 , 167 , 147 , 83 , 217 , 30 , 113 , 51 , 142 , 125 , 219 , 97 , 60 , 5 , 135 , 195 , 95 , 133 , 21 , 197 , 150 , 82 , 134 , 93 , 198 , 97 , 97 , 49 , 117 , 24 ,
242 , 253 , 242 , 5 , 190 , 71 , 182 , 1 , 0 , 69 , 25 , 181 , 139 , 84 , 242 , 79 , 150 , 158 , 29 , 215 , 98 , 100 , 245 , 16 , 86 , 165 , 18 , 98 , 46 , 100 , 139 , 19
} ;
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t delta8_2 [ 128 ] = {
236 , 22 , 153 , 161 , 50 , 141 , 15 , 130 , 89 , 251 , 33 , 5 , 140 , 201 , 225 , 194 , 138 , 76 , 248 , 89 , 25 , 26 , 29 , 93 , 250 , 251 , 48 , 157 , 41 , 126 , 140 , 152 ,
170 , 177 , 134 , 14 , 234 , 99 , 3 , 105 , 217 , 76 , 38 , 233 , 89 , 30 , 93 , 48 , 234 , 40 , 202 , 80 , 184 , 4 , 250 , 71 , 183 , 249 , 76 , 78 , 184 , 148 , 185 , 120 ,
137 , 214 , 238 , 57 , 50 , 93 , 29 , 60 , 99 , 207 , 40 , 15 , 43 , 28 , 177 , 118 , 60 , 231 , 90 , 47 , 198 , 251 , 250 , 241 , 212 , 114 , 249 , 17 , 95 , 161 , 216 , 218 ,
51 , 178 , 137 , 161 , 213 , 108 , 35 , 72 , 65 , 24 , 5 , 176 , 110 , 15 , 0 , 2 , 137 , 58 , 0 , 133 , 197 , 1 , 122 , 169 , 175 , 33 , 223 , 138 , 37 , 114 , 52 , 186
} ;
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t delta8_gray8_exp [ 128 ] = {
15 , 0 , 81 , 93 , 42 , 1 , 0 , 102 , 77 , 238 , 170 , 51 , 48 , 49 , 146 , 31 , 119 , 17 , 7 , 56 , 228 , 7 , 58 , 111 , 153 , 83 , 181 , 23 , 38 , 18 , 65 , 25 ,
129 , 69 , 69 , 135 , 230 , 12 , 13 , 135 , 161 , 26 , 97 , 169 , 64 , 27 , 126 , 166 , 5 , 15 , 33 , 100 , 17 , 41 , 7 , 15 , 8 , 130 , 69 , 172 , 82 , 3 , 112 , 88 ,
70 , 1 , 49 , 110 , 97 , 10 , 188 , 30 , 14 , 156 , 102 , 110 , 176 , 69 , 117 , 113 , 75 , 36 , 5 , 86 , 177 , 54 , 100 , 159 , 78 , 21 , 51 , 80 , 2 , 112 , 99 , 194 ,
191 , 75 , 105 , 156 , 23 , 37 , 147 , 71 , 65 , 45 , 20 , 5 , 29 , 69 , 242 , 77 , 13 , 100 , 29 , 82 , 99 , 99 , 123 , 153 , 89 , 132 , 205 , 40 , 9 , 14 , 87 , 167
} ;
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t delta8_rgba_exp [ 32 ] = {
2017-05-11 19:13:04 +08:00
13 , 11 , 189 , 60 , 41 , 68 , 112 , 28 , 84 , 66 , 68 , 48 , 14 , 30 , 91 , 36 , 24 , 54 , 113 , 101 , 41 , 90 , 39 , 82 , 107 , 47 , 46 , 80 , 69 , 102 , 130 , 21
2017-04-16 15:57:37 +08:00
} ;
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t delta8_gray8_res [ 128 ] ;
2017-05-10 22:37:25 +08:00
__attribute__ ( ( aligned ( 64 ) ) ) uint8_t delta8_rgba_res [ 32 ] ;
2017-04-16 15:57:37 +08:00
/* Run the delta8 grayscale function */
2017-05-11 19:13:04 +08:00
( * fptr_delta8_gray8 ) ( delta8_1 , delta8_2 , delta8_gray8_res , 128 ) ;
2017-04-16 15:57:37 +08:00
/* Compare results with expected results */
2017-08-11 03:44:20 +08:00
for ( int i = 0 ; i < 128 ; i + + ) {
if ( abs ( delta8_gray8_exp [ i ] - delta8_gray8_res [ i ] ) > 7 ) {
2017-05-11 19:13:04 +08:00
Panic ( " Delta grayscale function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u " , i , delta8_gray8_exp [ i ] , delta8_gray8_res [ i ] ) ;
2017-04-16 15:57:37 +08:00
}
}
/* Run the delta8 RGBA function */
2020-07-23 05:30:28 +08:00
( * fptr_delta8_rgba ) ( delta8_1 , delta8_2 , delta8_rgba_res , 32 ) ;
2017-04-16 15:57:37 +08:00
/* Compare results with expected results */
2017-08-11 03:44:20 +08:00
for ( int i = 0 ; i < 32 ; i + + ) {
if ( abs ( delta8_rgba_exp [ i ] - delta8_rgba_res [ i ] ) > 7 ) {
2017-05-11 19:13:04 +08:00
Panic ( " Delta RGBA function failed self-test: Results differ from the expected results. Column %u Expected %u Got %u " , i , delta8_rgba_exp [ i ] , delta8_rgba_res [ i ] ) ;
2017-04-16 15:57:37 +08:00
}
}
2017-05-10 22:37:25 +08:00
2019-09-17 22:31:48 +08:00
/*
2017-03-15 04:36:26 +08:00
SSSE3 deinterlacing functions were removed because they were usually equal
or slower than the standard code ( compiled with - O2 or better )
2017-05-10 22:37:25 +08:00
The function is too complicated to be vectorized efficiently on SSSE3
*/
2017-03-15 04:36:26 +08:00
fptr_deinterlace_4field_rgba = & std_deinterlace_4field_rgba ;
fptr_deinterlace_4field_bgra = & std_deinterlace_4field_bgra ;
fptr_deinterlace_4field_argb = & std_deinterlace_4field_argb ;
fptr_deinterlace_4field_abgr = & std_deinterlace_4field_abgr ;
fptr_deinterlace_4field_gray8 = & std_deinterlace_4field_gray8 ;
2020-07-20 05:50:47 +08:00
Debug ( 4 , " Deinterlace: Using standard functions " ) ;
2017-04-28 22:34:48 +08:00
2017-03-15 04:41:35 +08:00
# if defined(__i386__) && !defined(__x86_64__)
2016-04-04 22:11:48 +08:00
/* Use SSE2 aligned memory copy? */
2020-07-20 05:50:47 +08:00
if ( config . cpu_extensions & & sse_version > = 20 ) {
2016-04-04 22:11:48 +08:00
fptr_imgbufcpy = & sse2_aligned_memcpy ;
2020-07-08 04:13:41 +08:00
Debug ( 4 , " Image buffer copy: Using SSE2 aligned memcpy " ) ;
2016-04-04 22:11:48 +08:00
} else {
fptr_imgbufcpy = & memcpy ;
2020-07-08 04:13:41 +08:00
Debug ( 4 , " Image buffer copy: Using standard memcpy " ) ;
2016-04-04 22:11:48 +08:00
}
2017-03-15 04:41:35 +08:00
# else
fptr_imgbufcpy = & memcpy ;
2020-07-08 04:13:41 +08:00
Debug ( 4 , " Image buffer copy: Using standard memcpy " ) ;
2017-03-15 04:41:35 +08:00
# endif
2017-04-28 22:34:48 +08:00
2016-09-01 08:36:14 +08:00
y_table = y_table_global ;
uv_table = uv_table_global ;
r_v_table = r_v_table_global ;
g_v_table = g_v_table_global ;
g_u_table = g_u_table_global ;
b_u_table = b_u_table_global ;
2017-04-28 22:34:48 +08:00
2020-11-28 22:48:17 +08:00
int res = font . ReadFontFile ( config . font_file_location ) ;
2020-12-04 02:35:12 +08:00
if ( res = = - 1 ) {
Panic ( " Invalid font location: %s " , config . font_file_location ) ;
} else if ( res = = - 2 | | res = = - 3 | | res = = - 4 ) {
2020-11-28 22:14:45 +08:00
Panic ( " Invalid font file. " ) ;
}
2016-04-04 22:11:48 +08:00
initialised = true ;
2013-03-17 07:45:21 +08:00
}
2015-04-19 18:38:23 +08:00
/* Requests a writeable buffer to the image. This is safer than buffer() because this way we can guarantee that a buffer of required size exists */
2020-07-23 05:30:28 +08:00
uint8_t * Image : : WriteBuffer (
const unsigned int p_width ,
const unsigned int p_height ,
const unsigned int p_colours ,
const unsigned int p_subpixelorder ) {
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
if ( p_colours ! = ZM_COLOUR_GRAY8
& &
p_colours ! = ZM_COLOUR_RGB24
& &
p_colours ! = ZM_COLOUR_RGB32 ) {
2020-07-08 04:13:41 +08:00
Error ( " WriteBuffer called with unexpected colours: %d " , p_colours ) ;
2020-08-26 07:45:48 +08:00
return nullptr ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2019-08-11 02:46:05 +08:00
if ( ! ( p_height > 0 & & p_width > 0 ) ) {
Error ( " WriteBuffer called with invalid width or height: %d %d " , p_width , p_height ) ;
2020-08-26 07:45:48 +08:00
return nullptr ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2017-08-11 03:44:20 +08:00
if ( p_width ! = width | | p_height ! = height | | p_colours ! = colours | | p_subpixelorder ! = subpixelorder ) {
2020-07-24 05:14:56 +08:00
2017-11-17 20:52:26 +08:00
unsigned int newsize = ( p_width * p_height ) * p_colours ;
2017-04-28 22:34:48 +08:00
2020-08-26 07:45:48 +08:00
if ( buffer = = nullptr ) {
2016-04-04 22:11:48 +08:00
AllocImgBuffer ( newsize ) ;
} else {
2017-08-11 03:44:20 +08:00
if ( allocation < newsize ) {
if ( holdbuffer ) {
2016-04-04 22:11:48 +08:00
Error ( " Held buffer is undersized for requested buffer " ) ;
2020-08-26 07:45:48 +08:00
return nullptr ;
2016-04-04 22:11:48 +08:00
} else {
/* Replace buffer with a bigger one */
//DumpImgBuffer(); // Done in AllocImgBuffer too
AllocImgBuffer ( newsize ) ;
}
}
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
width = p_width ;
height = p_height ;
colours = p_colours ;
2020-07-24 05:14:56 +08:00
linesize = p_width * p_colours ;
2016-04-04 22:11:48 +08:00
subpixelorder = p_subpixelorder ;
pixels = height * width ;
2019-08-11 02:46:05 +08:00
size = newsize ;
2020-07-08 04:13:41 +08:00
} // end if need to re-alloc buffer
2017-04-28 22:34:48 +08:00
2019-09-17 22:31:48 +08:00
return buffer ;
2013-03-17 07:45:21 +08:00
}
2020-07-17 03:34:29 +08:00
/* Assign an existing buffer to the image instead of copying from a source buffer.
The goal is to reduce the amount of memory copying and increase efficiency and buffer reusing .
*/
2020-07-17 02:50:50 +08:00
void Image : : AssignDirect (
const unsigned int p_width ,
const unsigned int p_height ,
const unsigned int p_colours ,
const unsigned int p_subpixelorder ,
uint8_t * new_buffer ,
const size_t buffer_size ,
const int p_buffertype ) {
2020-07-17 03:34:29 +08:00
2020-08-26 07:45:48 +08:00
if ( new_buffer = = nullptr ) {
2016-04-04 22:11:48 +08:00
Error ( " Attempt to directly assign buffer from a NULL pointer " ) ;
return ;
}
2017-08-11 03:44:20 +08:00
if ( ! p_height | | ! p_width ) {
2020-07-08 04:13:41 +08:00
Error ( " Attempt to directly assign buffer with invalid width or height: %d %d " , p_width , p_height ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2017-08-11 03:44:20 +08:00
if ( p_colours ! = ZM_COLOUR_GRAY8 & & p_colours ! = ZM_COLOUR_RGB24 & & p_colours ! = ZM_COLOUR_RGB32 ) {
2021-02-03 06:26:44 +08:00
Error ( " Attempt to directly assign buffer with unexpected colours per pixel: %d " , p_colours ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2021-02-03 06:26:44 +08:00
unsigned int new_buffer_size = p_width * p_height * p_colours ;
2017-04-28 22:34:48 +08:00
2017-08-11 03:44:20 +08:00
if ( buffer_size < new_buffer_size ) {
2020-07-08 04:13:41 +08:00
Error ( " Attempt to directly assign buffer from an undersized buffer of size: %zu, needed %dx%d*%d colours = %zu " ,
buffer_size , p_width , p_height , p_colours , new_buffer_size ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2017-04-28 22:34:48 +08:00
2017-08-11 03:44:20 +08:00
if ( holdbuffer & & buffer ) {
if ( new_buffer_size > allocation ) {
2016-04-04 22:11:48 +08:00
Error ( " Held buffer is undersized for assigned buffer " ) ;
return ;
} else {
/* Copy into the held buffer */
2020-07-25 03:26:28 +08:00
if ( new_buffer ! = buffer ) {
2016-04-04 22:11:48 +08:00
( * fptr_imgbufcpy ) ( buffer , new_buffer , size ) ;
2020-07-25 03:26:28 +08:00
}
2016-04-04 22:11:48 +08:00
/* Free the new buffer */
DumpBuffer ( new_buffer , p_buffertype ) ;
}
} else {
/* Free an existing buffer if any */
DumpImgBuffer ( ) ;
allocation = buffer_size ;
buffertype = p_buffertype ;
buffer = new_buffer ;
}
2021-02-03 06:26:44 +08:00
width = p_width ;
height = p_height ;
colours = p_colours ;
linesize = width * colours ;
subpixelorder = p_subpixelorder ;
pixels = width * height ;
size = new_buffer_size ;
2020-07-25 00:41:27 +08:00
} // end void Image::AssignDirect
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
void Image : : Assign (
const unsigned int p_width ,
const unsigned int p_height ,
const unsigned int p_colours ,
const unsigned int p_subpixelorder ,
const uint8_t * new_buffer ,
const size_t buffer_size ) {
2017-04-28 22:34:48 +08:00
2020-08-26 07:45:48 +08:00
if ( new_buffer = = nullptr ) {
2016-04-04 22:11:48 +08:00
Error ( " Attempt to assign buffer from a NULL pointer " ) ;
return ;
}
2017-04-28 22:34:48 +08:00
2021-02-03 06:26:44 +08:00
unsigned int new_size = p_width * p_height * p_colours ;
2017-08-11 03:44:20 +08:00
if ( buffer_size < new_size ) {
2020-07-08 04:13:41 +08:00
Error ( " Attempt to assign buffer from an undersized buffer of size: %zu " , buffer_size ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2017-04-28 22:34:48 +08:00
2017-08-11 03:44:20 +08:00
if ( ! p_height | | ! p_width ) {
2020-07-08 04:13:41 +08:00
Error ( " Attempt to assign buffer with invalid width or height: %d %d " , p_width , p_height ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2017-04-28 22:34:48 +08:00
2017-08-11 03:44:20 +08:00
if ( p_colours ! = ZM_COLOUR_GRAY8 & & p_colours ! = ZM_COLOUR_RGB24 & & p_colours ! = ZM_COLOUR_RGB32 ) {
2020-07-08 04:13:41 +08:00
Error ( " Attempt to assign buffer with unexpected colours per pixel: %d " , p_colours ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2017-04-28 22:34:48 +08:00
2017-08-11 03:44:20 +08:00
if ( ! buffer | | p_width ! = width | | p_height ! = height | | p_colours ! = colours | | p_subpixelorder ! = subpixelorder ) {
2016-04-04 22:11:48 +08:00
2017-08-11 03:44:20 +08:00
if ( holdbuffer & & buffer ) {
if ( new_size > allocation ) {
2016-04-04 22:11:48 +08:00
Error ( " Held buffer is undersized for assigned buffer " ) ;
return ;
}
} else {
2020-11-20 05:37:28 +08:00
if ( ( new_size > allocation ) | | ! buffer ) {
2016-04-04 22:11:48 +08:00
DumpImgBuffer ( ) ;
AllocImgBuffer ( new_size ) ;
}
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
width = p_width ;
height = p_height ;
pixels = width * height ;
colours = p_colours ;
subpixelorder = p_subpixelorder ;
size = new_size ;
}
2017-04-28 22:34:48 +08:00
2017-11-14 06:24:28 +08:00
if ( new_buffer ! = buffer )
2016-04-04 22:11:48 +08:00
( * fptr_imgbufcpy ) ( buffer , new_buffer , size ) ;
2019-02-26 22:38:26 +08:00
update_function_pointers ( ) ;
2013-03-17 07:45:21 +08:00
}
2020-07-23 05:30:28 +08:00
void Image : : Assign ( const Image & image ) {
unsigned int new_size = image . height * image . linesize ;
2017-04-28 22:34:48 +08:00
2020-08-26 07:45:48 +08:00
if ( image . buffer = = nullptr ) {
2016-04-04 22:11:48 +08:00
Error ( " Attempt to assign image with an empty buffer " ) ;
return ;
}
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
if ( image . colours ! = ZM_COLOUR_GRAY8
& &
image . colours ! = ZM_COLOUR_RGB24
& &
image . colours ! = ZM_COLOUR_RGB32 ) {
Error ( " Attempt to assign image with unexpected colours per pixel: %d " , image . colours ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
if ( ! buffer
| | image . width ! = width | | image . height ! = height
| | image . colours ! = colours | | image . subpixelorder ! = subpixelorder
) {
2016-04-04 22:11:48 +08:00
2017-11-10 03:50:20 +08:00
if ( holdbuffer & & buffer ) {
if ( new_size > allocation ) {
2016-04-04 22:11:48 +08:00
Error ( " Held buffer is undersized for assigned buffer " ) ;
return ;
}
} else {
2017-11-10 03:50:20 +08:00
if ( new_size > allocation | | ! buffer ) {
2016-04-04 22:11:48 +08:00
// DumpImgBuffer(); This is also done in AllocImgBuffer
AllocImgBuffer ( new_size ) ;
}
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
width = image . width ;
height = image . height ;
pixels = width * height ;
colours = image . colours ;
subpixelorder = image . subpixelorder ;
size = new_size ;
2020-07-23 05:30:28 +08:00
linesize = image . linesize ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2017-11-10 03:50:20 +08:00
if ( image . buffer ! = buffer )
2016-04-04 22:11:48 +08:00
( * fptr_imgbufcpy ) ( buffer , image . buffer , size ) ;
2013-03-17 07:45:21 +08:00
}
2020-07-23 05:30:28 +08:00
Image * Image : : HighlightEdges (
Rgb colour ,
unsigned int p_colours ,
unsigned int p_subpixelorder ,
const Box * limits
) {
2018-08-12 01:35:37 +08:00
if ( colours ! = ZM_COLOUR_GRAY8 ) {
Panic ( " Attempt to highlight image edges when colours = %d " , colours ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Convert the colour's RGBA subpixel order into the image's subpixel order */
2020-07-08 04:13:41 +08:00
colour = rgb_convert ( colour , p_subpixelorder ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Create a new image of the target format */
2020-07-23 05:30:28 +08:00
Image * high_image = new Image ( width , height , p_colours , p_subpixelorder ) ;
2016-04-04 22:11:48 +08:00
uint8_t * high_buff = high_image - > WriteBuffer ( width , height , p_colours , p_subpixelorder ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Set image to all black */
high_image - > Clear ( ) ;
2013-03-17 07:45:21 +08:00
2020-07-08 04:13:41 +08:00
unsigned int lo_x = limits ? limits - > Lo ( ) . X ( ) : 0 ;
unsigned int lo_y = limits ? limits - > Lo ( ) . Y ( ) : 0 ;
unsigned int hi_x = limits ? limits - > Hi ( ) . X ( ) : width - 1 ;
unsigned int hi_y = limits ? limits - > Hi ( ) . Y ( ) : height - 1 ;
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
if ( p_colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
const uint8_t * p = buffer + ( y * linesize ) + lo_x ;
uint8_t * phigh = high_buff + ( y * linesize ) + lo_x ;
for ( unsigned int x = lo_x ; x < = hi_x ; x + + , p + + , phigh + + ) {
2016-04-04 22:11:48 +08:00
bool edge = false ;
2020-07-08 04:13:41 +08:00
if ( * p ) {
2020-07-23 05:30:28 +08:00
edge = ( x > 0 & & ! * ( p - 1 ) ) | | ( x < ( width - 1 ) & & ! * ( p + 1 ) ) | | ( y > 0 & & ! * ( p - width ) ) | | ( y < ( height - 1 ) & & ! * ( p + width ) ) ;
#if 0
2016-04-04 22:11:48 +08:00
if ( ! edge & & x > 0 & & ! * ( p - 1 ) ) edge = true ;
if ( ! edge & & x < ( width - 1 ) & & ! * ( p + 1 ) ) edge = true ;
if ( ! edge & & y > 0 & & ! * ( p - width ) ) edge = true ;
if ( ! edge & & y < ( height - 1 ) & & ! * ( p + width ) ) edge = true ;
2020-07-23 05:30:28 +08:00
# endif
2016-04-04 22:11:48 +08:00
}
2020-07-08 04:13:41 +08:00
if ( edge ) {
2016-04-04 22:11:48 +08:00
* phigh = colour ;
}
}
2013-03-17 07:45:21 +08:00
}
2020-07-08 04:13:41 +08:00
} else if ( p_colours = = ZM_COLOUR_RGB24 ) {
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
const uint8_t * p = buffer + ( y * linesize ) + lo_x ;
uint8_t * phigh = high_buff + ( ( ( y * linesize ) + lo_x ) * 3 ) ;
for ( unsigned int x = lo_x ; x < = hi_x ; x + + , p + + , phigh + = 3 ) {
2016-04-04 22:11:48 +08:00
bool edge = false ;
2020-07-08 04:13:41 +08:00
if ( * p ) {
2020-07-23 05:30:28 +08:00
edge = ( x > 0 & & ! * ( p - 1 ) ) | | ( x < ( width - 1 ) & & ! * ( p + 1 ) ) | | ( y > 0 & & ! * ( p - width ) ) | | ( y < ( height - 1 ) & & ! * ( p + width ) ) ;
#if 0
2016-04-04 22:11:48 +08:00
if ( ! edge & & x > 0 & & ! * ( p - 1 ) ) edge = true ;
if ( ! edge & & x < ( width - 1 ) & & ! * ( p + 1 ) ) edge = true ;
if ( ! edge & & y > 0 & & ! * ( p - width ) ) edge = true ;
if ( ! edge & & y < ( height - 1 ) & & ! * ( p + width ) ) edge = true ;
2020-07-23 05:30:28 +08:00
# endif
2016-04-04 22:11:48 +08:00
}
2020-07-08 04:13:41 +08:00
if ( edge ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( phigh ) = RED_VAL_RGBA ( colour ) ;
GREEN_PTR_RGBA ( phigh ) = GREEN_VAL_RGBA ( colour ) ;
BLUE_PTR_RGBA ( phigh ) = BLUE_VAL_RGBA ( colour ) ;
}
}
}
2020-07-08 04:13:41 +08:00
} else if ( p_colours = = ZM_COLOUR_RGB32 ) {
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
const uint8_t * p = buffer + ( y * linesize ) + lo_x ;
Rgb * phigh = ( Rgb * ) ( high_buff + ( ( ( y * linesize ) + lo_x ) * 4 ) ) ;
for ( unsigned int x = lo_x ; x < = hi_x ; x + + , p + + , phigh + + ) {
2016-04-04 22:11:48 +08:00
bool edge = false ;
2020-07-08 04:13:41 +08:00
if ( * p ) {
2020-07-23 05:30:28 +08:00
edge = ( x > 0 & & ! * ( p - 1 ) ) | | ( x < ( width - 1 ) & & ! * ( p + 1 ) ) | | ( y > 0 & & ! * ( p - width ) ) | | ( y < ( height - 1 ) & & ! * ( p + width ) ) ;
#if 0
2016-04-04 22:11:48 +08:00
if ( ! edge & & x > 0 & & ! * ( p - 1 ) ) edge = true ;
if ( ! edge & & x < ( width - 1 ) & & ! * ( p + 1 ) ) edge = true ;
if ( ! edge & & y > 0 & & ! * ( p - width ) ) edge = true ;
if ( ! edge & & y < ( height - 1 ) & & ! * ( p + width ) ) edge = true ;
2020-07-23 05:30:28 +08:00
# endif
2016-04-04 22:11:48 +08:00
}
2020-07-08 04:13:41 +08:00
if ( edge ) {
2016-04-04 22:11:48 +08:00
* phigh = colour ;
}
}
}
}
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
return high_image ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
2020-07-23 05:30:28 +08:00
bool Image : : ReadRaw ( const char * filename ) {
2016-04-04 22:11:48 +08:00
FILE * infile ;
2020-08-26 07:45:48 +08:00
if ( ( infile = fopen ( filename , " rb " ) ) = = nullptr ) {
2017-12-13 02:35:19 +08:00
Error ( " Can't open %s: %s " , filename , strerror ( errno ) ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
struct stat statbuf ;
2017-12-13 02:35:19 +08:00
if ( fstat ( fileno ( infile ) , & statbuf ) < 0 ) {
fclose ( infile ) ;
Error ( " Can't fstat %s: %s " , filename , strerror ( errno ) ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2018-03-21 02:49:01 +08:00
if ( ( unsigned int ) statbuf . st_size ! = size ) {
2017-12-13 02:35:19 +08:00
fclose ( infile ) ;
Error ( " Raw file size mismatch, expected %d bytes, found %ld " , size , statbuf . st_size ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2017-12-13 02:35:19 +08:00
if ( fread ( buffer , size , 1 , infile ) < 1 ) {
fclose ( infile ) ;
Error ( " Unable to read from '%s': %s " , filename , strerror ( errno ) ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
fclose ( infile ) ;
2016-04-04 22:11:48 +08:00
2017-12-13 02:35:19 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
2019-09-17 22:31:48 +08:00
bool Image : : WriteRaw ( const char * filename ) const {
2016-04-04 22:11:48 +08:00
FILE * outfile ;
2020-08-26 07:45:48 +08:00
if ( ( outfile = fopen ( filename , " wb " ) ) = = nullptr ) {
2019-09-17 22:31:48 +08:00
Error ( " Can't open %s: %s " , filename , strerror ( errno ) ) ;
2017-12-13 02:18:57 +08:00
return false ;
2016-04-04 22:11:48 +08:00
}
2017-12-13 02:18:57 +08:00
if ( fwrite ( buffer , size , 1 , outfile ) ! = 1 ) {
2019-09-17 22:31:48 +08:00
Error ( " Unable to write to '%s': %s " , filename , strerror ( errno ) ) ;
fclose ( outfile ) ;
2017-12-13 02:18:57 +08:00
return false ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
fclose ( outfile ) ;
2016-04-04 22:11:48 +08:00
2017-12-13 02:18:57 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
2018-04-04 03:23:54 +08:00
bool Image : : ReadJpeg ( const char * filename , unsigned int p_colours , unsigned int p_subpixelorder ) {
2016-04-04 22:11:48 +08:00
unsigned int new_width , new_height , new_colours , new_subpixelorder ;
2016-07-04 03:59:05 +08:00
struct jpeg_decompress_struct * cinfo = readjpg_dcinfo ;
2016-04-04 22:11:48 +08:00
2018-04-04 03:23:54 +08:00
if ( ! cinfo ) {
2016-07-04 03:59:05 +08:00
cinfo = readjpg_dcinfo = new jpeg_decompress_struct ;
2018-04-04 03:23:54 +08:00
cinfo - > err = jpeg_std_error ( & jpg_err . pub ) ;
2016-04-04 22:11:48 +08:00
jpg_err . pub . error_exit = zm_jpeg_error_exit ;
jpg_err . pub . emit_message = zm_jpeg_emit_message ;
2018-04-04 03:23:54 +08:00
jpeg_create_decompress ( cinfo ) ;
2016-04-04 22:11:48 +08:00
}
FILE * infile ;
2020-08-26 07:45:48 +08:00
if ( ( infile = fopen ( filename , " rb " ) ) = = nullptr ) {
2018-04-04 03:23:54 +08:00
Error ( " Can't open %s: %s " , filename , strerror ( errno ) ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2018-04-04 03:23:54 +08:00
if ( setjmp ( jpg_err . setjmp_buffer ) ) {
jpeg_abort_decompress ( cinfo ) ;
fclose ( infile ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2018-04-04 03:23:54 +08:00
jpeg_stdio_src ( cinfo , infile ) ;
2016-04-04 22:11:48 +08:00
2018-04-04 03:23:54 +08:00
jpeg_read_header ( cinfo , TRUE ) ;
2016-04-04 22:11:48 +08:00
2020-11-20 05:37:28 +08:00
if ( ( cinfo - > num_components ! = 1 ) & & ( cinfo - > num_components ! = 3 ) ) {
Error ( " Unexpected colours when reading jpeg image: %d " , colours ) ;
2018-04-04 03:23:54 +08:00
jpeg_abort_decompress ( cinfo ) ;
fclose ( infile ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Check if the image has at least one huffman table defined. If not, use the standard ones */
/* This is required for the MJPEG capture palette of USB devices */
2020-08-26 07:45:48 +08:00
if ( cinfo - > dc_huff_tbl_ptrs [ 0 ] = = nullptr ) {
2016-04-04 22:11:48 +08:00
zm_use_std_huff_tables ( cinfo ) ;
}
new_width = cinfo - > image_width ;
new_height = cinfo - > image_height ;
2020-11-20 05:37:28 +08:00
if ( ( width ! = new_width ) | | ( height ! = new_height ) ) {
2020-07-08 04:13:41 +08:00
Debug ( 9 , " Image dimensions differ. Old: %ux%u New: %ux%u " , width , height , new_width , new_height ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2020-07-24 05:14:56 +08:00
switch ( p_colours ) {
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_GRAY8 :
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_GRAYSCALE ;
new_colours = ZM_COLOUR_GRAY8 ;
new_subpixelorder = ZM_SUBPIX_ORDER_NONE ;
break ;
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB32 :
2013-03-17 07:45:21 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
new_colours = ZM_COLOUR_RGB32 ;
2019-09-17 22:31:48 +08:00
if ( p_subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_BGRX ;
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA ;
2019-09-17 22:31:48 +08:00
} else if ( p_subpixelorder = = ZM_SUBPIX_ORDER_ARGB ) {
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_XRGB ;
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB ;
2019-09-17 22:31:48 +08:00
} else if ( p_subpixelorder = = ZM_SUBPIX_ORDER_ABGR ) {
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_XBGR ;
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR ;
} else {
/* Assume RGBA */
cinfo - > out_color_space = JCS_EXT_RGBX ;
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA ;
}
2019-09-17 22:31:48 +08:00
break ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
Warning ( " libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead. " ) ;
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB24 :
default :
2017-04-28 22:34:48 +08:00
new_colours = ZM_COLOUR_RGB24 ;
2019-09-17 22:31:48 +08:00
if ( p_subpixelorder = = ZM_SUBPIX_ORDER_BGR ) {
# ifdef JCS_EXTENSIONS
cinfo - > out_color_space = JCS_EXT_BGR ;
2017-04-28 22:34:48 +08:00
new_subpixelorder = ZM_SUBPIX_ORDER_BGR ;
2013-03-17 07:45:21 +08:00
# else
2019-09-17 22:31:48 +08:00
Warning ( " libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead. " ) ;
cinfo - > out_color_space = JCS_RGB ;
2017-04-28 22:34:48 +08:00
new_subpixelorder = ZM_SUBPIX_ORDER_RGB ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
} else {
/* Assume RGB */
/*
2013-03-17 07:45:21 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_RGB ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_RGB ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
*/
cinfo - > out_color_space = JCS_RGB ;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB ;
}
break ;
2020-07-24 05:14:56 +08:00
} // end switch p_colours
2017-04-28 22:34:48 +08:00
2020-08-26 07:45:48 +08:00
if ( WriteBuffer ( new_width , new_height , new_colours , new_subpixelorder ) = = nullptr ) {
2016-04-04 22:11:48 +08:00
Error ( " Failed requesting writeable buffer for reading JPEG image. " ) ;
2019-09-17 22:31:48 +08:00
jpeg_abort_decompress ( cinfo ) ;
fclose ( infile ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
jpeg_start_decompress ( cinfo ) ;
2016-04-04 22:11:48 +08:00
2020-07-25 03:26:28 +08:00
JSAMPROW row_pointer = buffer ;
2019-09-17 22:31:48 +08:00
while ( cinfo - > output_scanline < cinfo - > output_height ) {
jpeg_read_scanlines ( cinfo , & row_pointer , 1 ) ;
2020-07-25 03:26:28 +08:00
row_pointer + = linesize ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
jpeg_finish_decompress ( cinfo ) ;
2016-04-04 22:11:48 +08:00
2019-09-17 22:31:48 +08:00
fclose ( infile ) ;
2016-04-04 22:11:48 +08:00
2019-09-17 22:31:48 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
2019-05-17 03:37:03 +08:00
// Multiple calling formats to permit inclusion (or not) of non blocking, quality_override and timestamp (exif), with suitable defaults.
2015-07-24 07:34:39 +08:00
// Note quality=zero means default
2017-09-26 04:22:52 +08:00
bool Image : : WriteJpeg ( const char * filename , int quality_override ) const {
2020-07-25 00:41:27 +08:00
return Image : : WriteJpeg ( filename , quality_override , ( timeval ) { 0 , 0 } , false ) ;
2015-07-24 07:34:39 +08:00
}
2017-09-26 04:22:52 +08:00
bool Image : : WriteJpeg ( const char * filename ) const {
2020-07-25 00:41:27 +08:00
return Image : : WriteJpeg ( filename , 0 , ( timeval ) { 0 , 0 } , false ) ;
2015-07-24 07:34:39 +08:00
}
2019-05-17 03:37:03 +08:00
bool Image : : WriteJpeg ( const char * filename , bool on_blocking_abort ) const {
return Image : : WriteJpeg ( filename , 0 , ( timeval ) { 0 , 0 } , on_blocking_abort ) ;
}
2017-09-26 04:22:52 +08:00
bool Image : : WriteJpeg ( const char * filename , struct timeval timestamp ) const {
2020-07-25 00:41:27 +08:00
return Image : : WriteJpeg ( filename , 0 , timestamp , false ) ;
2015-07-24 07:34:39 +08:00
}
2018-05-04 01:54:18 +08:00
bool Image : : WriteJpeg ( const char * filename , int quality_override , struct timeval timestamp ) const {
2019-05-17 03:37:03 +08:00
return Image : : WriteJpeg ( filename , quality_override , timestamp , false ) ;
}
2020-07-08 04:13:41 +08:00
2019-05-17 03:37:03 +08:00
bool Image : : WriteJpeg ( const char * filename , int quality_override , struct timeval timestamp , bool on_blocking_abort ) const {
2019-09-17 22:31:48 +08:00
if ( config . colour_jpeg_files & & ( colours = = ZM_COLOUR_GRAY8 ) ) {
2018-05-04 01:54:18 +08:00
Image temp_image ( * this ) ;
temp_image . Colourise ( ZM_COLOUR_RGB24 , ZM_SUBPIX_ORDER_RGB ) ;
2019-05-17 03:37:03 +08:00
return temp_image . WriteJpeg ( filename , quality_override , timestamp , on_blocking_abort ) ;
2016-04-04 22:11:48 +08:00
}
2020-07-08 04:13:41 +08:00
int quality = quality_override ? quality_override : config . jpeg_file_quality ;
2016-04-04 22:11:48 +08:00
2016-07-04 03:59:05 +08:00
struct jpeg_compress_struct * cinfo = writejpg_ccinfo [ quality ] ;
2020-08-26 07:45:48 +08:00
FILE * outfile = nullptr ;
2021-02-28 01:27:27 +08:00
int raw_fd = 0 ;
2016-04-04 22:11:48 +08:00
2017-09-26 04:22:52 +08:00
if ( ! cinfo ) {
2016-07-04 03:59:05 +08:00
cinfo = writejpg_ccinfo [ quality ] = new jpeg_compress_struct ;
2019-09-17 22:31:48 +08:00
cinfo - > err = jpeg_std_error ( & jpg_err . pub ) ;
jpeg_create_compress ( cinfo ) ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
if ( ! on_blocking_abort ) {
jpg_err . pub . error_exit = zm_jpeg_error_exit ;
jpg_err . pub . emit_message = zm_jpeg_emit_message ;
2019-05-17 03:37:03 +08:00
} else {
jpg_err . pub . error_exit = zm_jpeg_error_silent ;
jpg_err . pub . emit_message = zm_jpeg_emit_silence ;
2019-09-17 22:31:48 +08:00
if ( setjmp ( jpg_err . setjmp_buffer ) ) {
jpeg_abort_compress ( cinfo ) ;
2020-08-26 07:45:48 +08:00
Debug ( 1 , " Aborted a write mid-stream and %s and %d " , ( outfile = = nullptr ) ? " closing file " : " file not opened " , raw_fd ) ;
2019-09-17 22:31:48 +08:00
if ( raw_fd )
2019-05-17 03:37:03 +08:00
close ( raw_fd ) ;
2019-09-17 22:31:48 +08:00
if ( outfile )
fclose ( outfile ) ;
return false ;
2019-05-17 03:37:03 +08:00
}
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
if ( ! on_blocking_abort ) {
2020-08-26 07:45:48 +08:00
if ( ( outfile = fopen ( filename , " wb " ) ) = = nullptr ) {
2019-09-17 22:31:48 +08:00
Error ( " Can't open %s for writing: %s " , filename , strerror ( errno ) ) ;
2019-05-17 03:37:03 +08:00
return false ;
}
} else {
2019-09-17 22:31:48 +08:00
raw_fd = open ( filename , O_WRONLY | O_NONBLOCK | O_CREAT | O_TRUNC , S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) ;
if ( raw_fd < 0 )
return false ;
outfile = fdopen ( raw_fd , " wb " ) ;
2020-08-26 07:45:48 +08:00
if ( outfile = = nullptr ) {
2019-05-17 03:37:03 +08:00
close ( raw_fd ) ;
2019-09-17 22:31:48 +08:00
return false ;
2019-05-17 03:37:03 +08:00
}
2016-04-04 22:11:48 +08:00
}
2019-05-17 03:37:03 +08:00
2019-09-17 22:31:48 +08:00
jpeg_stdio_dest ( cinfo , outfile ) ;
2016-04-04 22:11:48 +08:00
cinfo - > image_width = width ; /* image width and height, in pixels */
cinfo - > image_height = height ;
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
switch ( colours ) {
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_GRAY8 :
2017-04-28 22:34:48 +08:00
cinfo - > input_components = 1 ;
cinfo - > in_color_space = JCS_GRAYSCALE ;
break ;
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB32 :
2013-03-17 07:45:21 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
cinfo - > input_components = 4 ;
2020-07-25 03:26:28 +08:00
if ( subpixelorder = = ZM_SUBPIX_ORDER_RGBA ) {
cinfo - > in_color_space = JCS_EXT_RGBX ;
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_EXT_BGRX ;
2017-09-26 04:22:52 +08:00
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_ARGB ) {
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_EXT_XRGB ;
2017-09-26 04:22:52 +08:00
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_ABGR ) {
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_EXT_XBGR ;
} else {
2020-07-25 03:26:28 +08:00
Warning ( " Unknwon subpixelorder %d " , subpixelorder ) ;
2017-04-28 22:34:48 +08:00
/* Assume RGBA */
cinfo - > in_color_space = JCS_EXT_RGBX ;
2019-09-17 22:31:48 +08:00
}
break ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
Error ( " libjpeg-turbo is required for JPEG encoding directly from RGB32 source " ) ;
2019-09-17 22:31:48 +08:00
jpeg_abort_compress ( cinfo ) ;
2017-04-28 22:34:48 +08:00
fclose ( outfile ) ;
2017-09-26 04:22:52 +08:00
return false ;
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB24 :
default :
2017-04-28 22:34:48 +08:00
cinfo - > input_components = 3 ;
2017-09-26 04:22:52 +08:00
if ( subpixelorder = = ZM_SUBPIX_ORDER_BGR ) {
2019-09-17 22:31:48 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_EXT_BGR ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
Error ( " libjpeg-turbo is required for JPEG encoding directly from BGR24 source " ) ;
2019-09-17 22:31:48 +08:00
jpeg_abort_compress ( cinfo ) ;
2017-04-28 22:34:48 +08:00
fclose ( outfile ) ;
2019-09-17 22:31:48 +08:00
return false ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
} else {
/* Assume RGB */
/*
2013-03-17 07:45:21 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_RGB ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_RGB ;
2013-03-17 07:45:21 +08:00
# endif
2020-11-20 05:37:28 +08:00
*/
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_RGB ;
}
break ;
2019-09-17 22:31:48 +08:00
} // end switch(colours)
2017-04-28 22:34:48 +08:00
2019-09-17 22:31:48 +08:00
jpeg_set_defaults ( cinfo ) ;
jpeg_set_quality ( cinfo , quality , FALSE ) ;
2016-04-04 22:11:48 +08:00
cinfo - > dct_method = JDCT_FASTEST ;
2019-09-17 22:31:48 +08:00
jpeg_start_compress ( cinfo , TRUE ) ;
2017-04-28 22:34:48 +08:00
if ( config . add_jpeg_comments & & text [ 0 ] ) {
2019-09-17 22:31:48 +08:00
jpeg_write_marker ( cinfo , JPEG_COM , ( const JOCTET * ) text , strlen ( text ) ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
// If we have a non-zero time (meaning a parameter was passed in), then form a simple exif segment with that time as DateTimeOriginal and SubsecTimeOriginal
// No timestamp just leave off the exif section.
2017-09-26 04:22:52 +08:00
if ( timestamp . tv_sec ) {
2017-04-28 22:34:48 +08:00
# define EXIFTIMES_MS_OFFSET 0x36 // three decimal digits for milliseconds
# define EXIFTIMES_MS_LEN 0x03
# define EXIFTIMES_OFFSET 0x3E // 19 characters format '2015:07:21 13:14:45' not including quotes
# define EXIFTIMES_LEN 0x13 // = 19
# define EXIF_CODE 0xE1
// This is a lot of stuff to allocate on the stack. Recommend char *timebuf[64];
char timebuf [ 64 ] , msbuf [ 64 ] ;
2021-03-07 23:35:05 +08:00
tm timestamp_tm = { } ;
strftime ( timebuf , sizeof timebuf , " %Y:%m:%d %H:%M:%S " , localtime_r ( & timestamp . tv_sec , & timestamp_tm ) ) ;
2017-04-28 22:34:48 +08:00
snprintf ( msbuf , sizeof msbuf , " %06d " , ( int ) ( timestamp . tv_usec ) ) ; // we only use milliseconds because that's all defined in exif, but this is the whole microseconds because we have it
unsigned char exiftimes [ 82 ] = {
0x45 , 0x78 , 0x69 , 0x66 , 0x00 , 0x00 , 0x49 , 0x49 , 0x2A , 0x00 , 0x08 , 0x00 , 0x00 , 0x00 , 0x01 , 0x00 ,
0x69 , 0x87 , 0x04 , 0x00 , 0x01 , 0x00 , 0x00 , 0x00 , 0x1A , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 ,
0x02 , 0x00 , 0x03 , 0x90 , 0x02 , 0x00 , 0x14 , 0x00 , 0x00 , 0x00 , 0x38 , 0x00 , 0x00 , 0x00 , 0x91 , 0x92 ,
0x02 , 0x00 , 0x04 , 0x00 , 0x00 , 0x00 , 0xff , 0xff , 0xff , 0x00 , 0x00 , 0x00 , 0x00 , 0x00 , 0xff , 0xff ,
0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff , 0xff ,
0xff , 0x00 } ;
memcpy ( & exiftimes [ EXIFTIMES_OFFSET ] , timebuf , EXIFTIMES_LEN ) ;
memcpy ( & exiftimes [ EXIFTIMES_MS_OFFSET ] , msbuf , EXIFTIMES_MS_LEN ) ;
2019-09-17 22:31:48 +08:00
jpeg_write_marker ( cinfo , EXIF_CODE , ( const JOCTET * ) exiftimes , sizeof ( exiftimes ) ) ;
2017-04-28 22:34:48 +08:00
}
2013-03-17 07:45:21 +08:00
2020-07-26 02:25:45 +08:00
JSAMPROW row_pointer = buffer ; /* pointer to a single row */
2017-09-26 04:22:52 +08:00
while ( cinfo - > next_scanline < cinfo - > image_height ) {
2019-09-17 22:31:48 +08:00
jpeg_write_scanlines ( cinfo , & row_pointer , 1 ) ;
2020-07-25 03:26:28 +08:00
row_pointer + = linesize ;
2016-04-04 22:11:48 +08:00
}
2017-09-26 04:22:52 +08:00
jpeg_finish_compress ( cinfo ) ;
fclose ( outfile ) ;
2013-03-17 07:45:21 +08:00
2017-09-26 04:22:52 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
2019-09-17 22:31:48 +08:00
bool Image : : DecodeJpeg (
const JOCTET * inbuffer ,
int inbuffer_size ,
unsigned int p_colours ,
unsigned int p_subpixelorder )
2013-03-17 07:45:21 +08:00
{
2016-04-04 22:11:48 +08:00
unsigned int new_width , new_height , new_colours , new_subpixelorder ;
2016-07-04 03:59:05 +08:00
struct jpeg_decompress_struct * cinfo = decodejpg_dcinfo ;
2016-04-04 22:11:48 +08:00
2019-09-17 22:31:48 +08:00
if ( ! cinfo ) {
2016-07-04 03:59:05 +08:00
cinfo = decodejpg_dcinfo = new jpeg_decompress_struct ;
2016-04-04 22:11:48 +08:00
cinfo - > err = jpeg_std_error ( & jpg_err . pub ) ;
jpg_err . pub . error_exit = zm_jpeg_error_exit ;
jpg_err . pub . emit_message = zm_jpeg_emit_message ;
jpeg_create_decompress ( cinfo ) ;
}
2019-09-17 22:31:48 +08:00
if ( setjmp ( jpg_err . setjmp_buffer ) ) {
jpeg_abort_decompress ( cinfo ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
zm_jpeg_mem_src ( cinfo , inbuffer , inbuffer_size ) ;
2016-04-04 22:11:48 +08:00
2019-09-17 22:31:48 +08:00
jpeg_read_header ( cinfo , TRUE ) ;
2016-04-04 22:11:48 +08:00
2019-09-17 22:31:48 +08:00
if ( ( cinfo - > num_components ! = 1 ) & & ( cinfo - > num_components ! = 3 ) ) {
Error ( " Unexpected colours when reading jpeg image: %d " , colours ) ;
jpeg_abort_decompress ( cinfo ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Check if the image has at least one huffman table defined. If not, use the standard ones */
/* This is required for the MJPEG capture palette of USB devices */
2020-08-26 07:45:48 +08:00
if ( cinfo - > dc_huff_tbl_ptrs [ 0 ] = = nullptr ) {
2016-04-04 22:11:48 +08:00
zm_use_std_huff_tables ( cinfo ) ;
}
new_width = cinfo - > image_width ;
new_height = cinfo - > image_height ;
2020-11-20 05:37:28 +08:00
if ( ( width ! = new_width ) | | ( height ! = new_height ) ) {
2019-09-17 22:31:48 +08:00
Debug ( 9 , " Image dimensions differ. Old: %ux%u New: %ux%u " ,
width , height , new_width , new_height ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2019-09-17 22:31:48 +08:00
switch ( p_colours ) {
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_GRAY8 :
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_GRAYSCALE ;
new_colours = ZM_COLOUR_GRAY8 ;
new_subpixelorder = ZM_SUBPIX_ORDER_NONE ;
break ;
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB32 :
2013-03-17 07:45:21 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
new_colours = ZM_COLOUR_RGB32 ;
2019-09-17 22:31:48 +08:00
if ( p_subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_BGRX ;
new_subpixelorder = ZM_SUBPIX_ORDER_BGRA ;
2019-09-17 22:31:48 +08:00
} else if ( p_subpixelorder = = ZM_SUBPIX_ORDER_ARGB ) {
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_XRGB ;
new_subpixelorder = ZM_SUBPIX_ORDER_ARGB ;
2019-09-17 22:31:48 +08:00
} else if ( p_subpixelorder = = ZM_SUBPIX_ORDER_ABGR ) {
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_XBGR ;
new_subpixelorder = ZM_SUBPIX_ORDER_ABGR ;
} else {
/* Assume RGBA */
cinfo - > out_color_space = JCS_EXT_RGBX ;
new_subpixelorder = ZM_SUBPIX_ORDER_RGBA ;
}
2019-09-17 22:31:48 +08:00
break ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
Warning ( " libjpeg-turbo is required for reading a JPEG directly into a RGB32 buffer, reading into a RGB24 buffer instead. " ) ;
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB24 :
default :
2017-04-28 22:34:48 +08:00
new_colours = ZM_COLOUR_RGB24 ;
2019-09-17 22:31:48 +08:00
if ( p_subpixelorder = = ZM_SUBPIX_ORDER_BGR ) {
# ifdef JCS_EXTENSIONS
cinfo - > out_color_space = JCS_EXT_BGR ;
2017-04-28 22:34:48 +08:00
new_subpixelorder = ZM_SUBPIX_ORDER_BGR ;
2013-03-17 07:45:21 +08:00
# else
2019-09-17 22:31:48 +08:00
Warning ( " libjpeg-turbo is required for reading a JPEG directly into a BGR24 buffer, reading into a RGB24 buffer instead. " ) ;
cinfo - > out_color_space = JCS_RGB ;
2017-04-28 22:34:48 +08:00
new_subpixelorder = ZM_SUBPIX_ORDER_RGB ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
} else {
/* Assume RGB */
/*
2013-03-17 07:45:21 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_RGB ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_RGB ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
*/
cinfo - > out_color_space = JCS_RGB ;
new_subpixelorder = ZM_SUBPIX_ORDER_RGB ;
}
break ;
2018-10-22 22:41:27 +08:00
} // end switch
2017-04-28 22:34:48 +08:00
2020-08-26 07:45:48 +08:00
if ( WriteBuffer ( new_width , new_height , new_colours , new_subpixelorder ) = = nullptr ) {
2016-04-04 22:11:48 +08:00
Error ( " Failed requesting writeable buffer for reading JPEG image. " ) ;
2019-09-17 22:31:48 +08:00
jpeg_abort_decompress ( cinfo ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
jpeg_start_decompress ( cinfo ) ;
2016-04-04 22:11:48 +08:00
2020-07-25 03:26:28 +08:00
JSAMPROW row_pointer = buffer ; /* pointer to a single row */
2018-10-22 22:41:27 +08:00
while ( cinfo - > output_scanline < cinfo - > output_height ) {
2019-09-17 22:31:48 +08:00
jpeg_read_scanlines ( cinfo , & row_pointer , 1 ) ;
2020-07-25 03:26:28 +08:00
row_pointer + = linesize ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
jpeg_finish_decompress ( cinfo ) ;
2016-04-04 22:11:48 +08:00
2018-10-22 22:41:27 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
2019-09-17 22:31:48 +08:00
bool Image : : EncodeJpeg ( JOCTET * outbuffer , int * outbuffer_size , int quality_override ) const {
if ( config . colour_jpeg_files & & ( colours = = ZM_COLOUR_GRAY8 ) ) {
Image temp_image ( * this ) ;
temp_image . Colourise ( ZM_COLOUR_RGB24 , ZM_SUBPIX_ORDER_RGB ) ;
return temp_image . EncodeJpeg ( outbuffer , outbuffer_size , quality_override ) ;
2016-04-04 22:11:48 +08:00
}
2020-07-08 04:13:41 +08:00
int quality = quality_override ? quality_override : config . jpeg_stream_quality ;
2016-04-04 22:11:48 +08:00
2016-07-04 03:59:05 +08:00
struct jpeg_compress_struct * cinfo = encodejpg_ccinfo [ quality ] ;
2016-04-04 22:11:48 +08:00
2018-10-22 22:41:27 +08:00
if ( ! cinfo ) {
2016-07-04 03:59:05 +08:00
cinfo = encodejpg_ccinfo [ quality ] = new jpeg_compress_struct ;
2019-09-17 22:31:48 +08:00
cinfo - > err = jpeg_std_error ( & jpg_err . pub ) ;
2016-04-04 22:11:48 +08:00
jpg_err . pub . error_exit = zm_jpeg_error_exit ;
jpg_err . pub . emit_message = zm_jpeg_emit_message ;
2019-09-17 22:31:48 +08:00
jpeg_create_compress ( cinfo ) ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
zm_jpeg_mem_dest ( cinfo , outbuffer , outbuffer_size ) ;
2016-04-04 22:11:48 +08:00
cinfo - > image_width = width ; /* image width and height, in pixels */
cinfo - > image_height = height ;
2020-07-25 03:26:28 +08:00
switch ( colours ) {
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_GRAY8 :
2017-04-28 22:34:48 +08:00
cinfo - > input_components = 1 ;
cinfo - > in_color_space = JCS_GRAYSCALE ;
break ;
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB32 :
2013-03-17 07:45:21 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
cinfo - > input_components = 4 ;
2020-07-25 03:26:28 +08:00
if ( subpixelorder = = ZM_SUBPIX_ORDER_RGBA ) {
cinfo - > in_color_space = JCS_EXT_RGBX ;
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_EXT_BGRX ;
2019-09-17 22:31:48 +08:00
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_ARGB ) {
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_EXT_XRGB ;
2019-09-17 22:31:48 +08:00
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_ABGR ) {
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_EXT_XBGR ;
} else {
2020-07-25 03:26:28 +08:00
Warning ( " unknown subpixelorder %d " , subpixelorder ) ;
2017-04-28 22:34:48 +08:00
/* Assume RGBA */
cinfo - > in_color_space = JCS_EXT_RGBX ;
2019-09-17 22:31:48 +08:00
}
break ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
Error ( " libjpeg-turbo is required for JPEG encoding directly from RGB32 source " ) ;
2019-09-17 22:31:48 +08:00
jpeg_abort_compress ( cinfo ) ;
return false ;
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB24 :
default :
2017-04-28 22:34:48 +08:00
cinfo - > input_components = 3 ;
2019-09-17 22:31:48 +08:00
if ( subpixelorder = = ZM_SUBPIX_ORDER_BGR ) {
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
cinfo - > in_color_space = JCS_EXT_BGR ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
Error ( " libjpeg-turbo is required for JPEG encoding directly from BGR24 source " ) ;
2019-09-17 22:31:48 +08:00
jpeg_abort_compress ( cinfo ) ;
return false ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
} else {
/* Assume RGB */
/*
2013-03-17 07:45:21 +08:00
# ifdef JCS_EXTENSIONS
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_EXT_RGB ;
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
cinfo - > out_color_space = JCS_RGB ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
*/
cinfo - > in_color_space = JCS_RGB ;
}
break ;
2018-10-22 22:41:27 +08:00
} // end switch
2017-04-28 22:34:48 +08:00
2019-09-17 22:31:48 +08:00
jpeg_set_defaults ( cinfo ) ;
jpeg_set_quality ( cinfo , quality , FALSE ) ;
2016-04-04 22:11:48 +08:00
cinfo - > dct_method = JDCT_FASTEST ;
2019-09-17 22:31:48 +08:00
jpeg_start_compress ( cinfo , TRUE ) ;
2016-04-04 22:11:48 +08:00
2020-07-25 03:26:28 +08:00
JSAMPROW row_pointer = buffer ;
2018-10-22 22:41:27 +08:00
while ( cinfo - > next_scanline < cinfo - > image_height ) {
2019-09-17 22:31:48 +08:00
jpeg_write_scanlines ( cinfo , & row_pointer , 1 ) ;
2020-07-25 03:26:28 +08:00
row_pointer + = linesize ;
2016-04-04 22:11:48 +08:00
}
2019-09-17 22:31:48 +08:00
jpeg_finish_compress ( cinfo ) ;
2016-04-04 22:11:48 +08:00
2018-10-22 22:41:27 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
# if HAVE_ZLIB_H
2018-10-22 22:41:27 +08:00
bool Image : : Unzip ( const Bytef * inbuffer , unsigned long inbuffer_size ) {
2016-04-04 22:11:48 +08:00
unsigned long zip_size = size ;
int result = uncompress ( buffer , & zip_size , inbuffer , inbuffer_size ) ;
2018-10-22 22:41:27 +08:00
if ( result ! = Z_OK ) {
Error ( " Unzip failed, result = %d " , result ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2018-10-22 22:41:27 +08:00
if ( zip_size ! = ( unsigned int ) size ) {
Error ( " Unzip failed, size mismatch, expected %d bytes, got %ld " , size , zip_size ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2018-10-22 22:41:27 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
2018-10-22 22:41:27 +08:00
bool Image : : Zip ( Bytef * outbuffer , unsigned long * outbuffer_size , int compression_level ) const {
2016-04-04 22:11:48 +08:00
int result = compress2 ( outbuffer , outbuffer_size , buffer , size , compression_level ) ;
2018-10-22 22:41:27 +08:00
if ( result ! = Z_OK ) {
Error ( " Zip failed, result = %d " , result ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2018-10-22 22:41:27 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
# endif // HAVE_ZLIB_H
2017-12-13 02:25:02 +08:00
bool Image : : Crop ( unsigned int lo_x , unsigned int lo_y , unsigned int hi_x , unsigned int hi_y ) {
2016-04-04 22:11:48 +08:00
unsigned int new_width = ( hi_x - lo_x ) + 1 ;
unsigned int new_height = ( hi_y - lo_y ) + 1 ;
2017-12-13 02:25:02 +08:00
if ( lo_x > hi_x | | lo_y > hi_y ) {
2020-07-08 04:13:41 +08:00
Error ( " Invalid or reversed crop region %d,%d -> %d,%d " , lo_x , lo_y , hi_x , hi_y ) ;
return false ;
2016-04-04 22:11:48 +08:00
}
2017-12-13 02:25:02 +08:00
if ( hi_x > ( width - 1 ) | | ( hi_y > ( height - 1 ) ) ) {
2020-07-08 04:13:41 +08:00
Error ( " Attempting to crop outside image, %d,%d -> %d,%d not in %d,%d " ,
lo_x , lo_y , hi_x , hi_y , width - 1 , height - 1 ) ;
2018-10-22 22:41:27 +08:00
return false ;
2016-04-04 22:11:48 +08:00
}
2021-02-03 06:26:44 +08:00
if ( ( new_width = = width ) & & ( new_height = = height ) ) {
2018-10-22 22:41:27 +08:00
return true ;
2016-04-04 22:11:48 +08:00
}
2021-02-03 06:26:44 +08:00
unsigned int new_stride = new_width * colours ;
unsigned int new_size = new_stride * new_height ;
2016-04-04 22:11:48 +08:00
uint8_t * new_buffer = AllocBuffer ( new_size ) ;
2017-04-28 22:34:48 +08:00
2017-12-13 02:25:02 +08:00
for ( unsigned int y = lo_y , ny = 0 ; y < = hi_y ; y + + , ny + + ) {
2021-02-03 12:19:06 +08:00
unsigned char * pbuf = & buffer [ ( ( y * linesize ) + ( lo_x * colours ) ) ] ;
2021-02-03 06:26:44 +08:00
unsigned char * pnbuf = & new_buffer [ ny * new_stride ] ;
memcpy ( pnbuf , pbuf , new_stride ) ;
2016-04-04 22:11:48 +08:00
}
AssignDirect ( new_width , new_height , colours , subpixelorder , new_buffer , new_size , ZM_BUFTYPE_ZM ) ;
2018-10-22 22:41:27 +08:00
return true ;
2013-03-17 07:45:21 +08:00
}
2020-07-08 04:13:41 +08:00
bool Image : : Crop ( const Box & limits ) {
return Crop ( limits . LoX ( ) , limits . LoY ( ) , limits . HiX ( ) , limits . HiY ( ) ) ;
2013-03-17 07:45:21 +08:00
}
/* Far from complete */
/* Need to implement all possible of overlays possible */
2018-10-22 22:41:27 +08:00
void Image : : Overlay ( const Image & image ) {
if ( ! ( width = = image . width & & height = = image . height ) ) {
Panic ( " Attempt to overlay different sized images, expected %dx%d, got %dx%d " ,
width , height , image . width , image . height ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( colours = = image . colours & & subpixelorder ! = image . subpixelorder ) {
2016-04-04 22:11:48 +08:00
Warning ( " Attempt to overlay images of same format but with different subpixel order. " ) ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Grayscale ontop of grayscale - complete */
if ( colours = = ZM_COLOUR_GRAY8 & & image . colours = = ZM_COLOUR_GRAY8 ) {
const uint8_t * const max_ptr = buffer + size ;
const uint8_t * psrc = image . buffer ;
uint8_t * pdest = buffer ;
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
while ( pdest < max_ptr ) {
2018-10-22 22:41:27 +08:00
if ( * psrc ) {
2016-04-04 22:11:48 +08:00
* pdest = * psrc ;
}
pdest + + ;
psrc + + ;
}
2017-04-28 22:34:48 +08:00
/* RGB24 ontop of grayscale - convert to same format first - complete */
2016-04-04 22:11:48 +08:00
} else if ( colours = = ZM_COLOUR_GRAY8 & & image . colours = = ZM_COLOUR_RGB24 ) {
Colourise ( image . colours , image . subpixelorder ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = buffer + size ;
const uint8_t * psrc = image . buffer ;
uint8_t * pdest = buffer ;
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
while ( pdest < max_ptr ) {
2018-10-22 22:41:27 +08:00
if ( RED_PTR_RGBA ( psrc ) | | GREEN_PTR_RGBA ( psrc ) | | BLUE_PTR_RGBA ( psrc ) ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( pdest ) = RED_PTR_RGBA ( psrc ) ;
GREEN_PTR_RGBA ( pdest ) = GREEN_PTR_RGBA ( psrc ) ;
BLUE_PTR_RGBA ( pdest ) = BLUE_PTR_RGBA ( psrc ) ;
}
pdest + = 3 ;
psrc + = 3 ;
}
2017-04-28 22:34:48 +08:00
/* RGB32 ontop of grayscale - convert to same format first - complete */
2020-07-08 04:13:41 +08:00
} else if ( colours = = ZM_COLOUR_GRAY8 & & image . colours = = ZM_COLOUR_RGB32 ) {
2016-04-04 22:11:48 +08:00
Colourise ( image . colours , image . subpixelorder ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
const Rgb * const max_ptr = ( Rgb * ) ( buffer + size ) ;
2019-09-17 22:31:48 +08:00
const Rgb * prsrc = ( Rgb * ) image . buffer ;
2016-04-04 22:11:48 +08:00
Rgb * prdest = ( Rgb * ) buffer ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( subpixelorder = = ZM_SUBPIX_ORDER_RGBA | | subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
2016-04-04 22:11:48 +08:00
/* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */
2020-07-08 04:13:41 +08:00
while ( prdest < max_ptr ) {
2018-10-22 22:41:27 +08:00
if ( RED_PTR_RGBA ( prsrc ) | | GREEN_PTR_RGBA ( prsrc ) | | BLUE_PTR_RGBA ( prsrc ) ) {
2016-04-04 22:11:48 +08:00
* prdest = * prsrc ;
}
prdest + + ;
prsrc + + ;
}
} else {
/* ABGR\ARGB subpixel order - Alpha byte is first */
2020-07-08 04:13:41 +08:00
while ( prdest < max_ptr ) {
2018-10-22 22:41:27 +08:00
if ( RED_PTR_ABGR ( prsrc ) | | GREEN_PTR_ABGR ( prsrc ) | | BLUE_PTR_ABGR ( prsrc ) ) {
2016-04-04 22:11:48 +08:00
* prdest = * prsrc ;
}
prdest + + ;
prsrc + + ;
}
}
2017-04-28 22:34:48 +08:00
/* Grayscale ontop of RGB24 - complete */
2016-04-04 22:11:48 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 & & image . colours = = ZM_COLOUR_GRAY8 ) {
const uint8_t * const max_ptr = buffer + size ;
const uint8_t * psrc = image . buffer ;
uint8_t * pdest = buffer ;
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
while ( pdest < max_ptr ) {
2018-10-22 22:41:27 +08:00
if ( * psrc ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( pdest ) = GREEN_PTR_RGBA ( pdest ) = BLUE_PTR_RGBA ( pdest ) = * psrc ;
}
pdest + = 3 ;
psrc + + ;
}
2017-04-28 22:34:48 +08:00
/* RGB24 ontop of RGB24 - not complete. need to take care of different subpixel orders */
2016-04-04 22:11:48 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 & & image . colours = = ZM_COLOUR_RGB24 ) {
const uint8_t * const max_ptr = buffer + size ;
const uint8_t * psrc = image . buffer ;
uint8_t * pdest = buffer ;
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
while ( pdest < max_ptr ) {
2018-10-22 22:41:27 +08:00
if ( RED_PTR_RGBA ( psrc ) | | GREEN_PTR_RGBA ( psrc ) | | BLUE_PTR_RGBA ( psrc ) ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( pdest ) = RED_PTR_RGBA ( psrc ) ;
GREEN_PTR_RGBA ( pdest ) = GREEN_PTR_RGBA ( psrc ) ;
BLUE_PTR_RGBA ( pdest ) = BLUE_PTR_RGBA ( psrc ) ;
}
pdest + = 3 ;
psrc + = 3 ;
2019-09-17 22:31:48 +08:00
}
2017-04-28 22:34:48 +08:00
/* RGB32 ontop of RGB24 - TO BE DONE */
2016-04-04 22:11:48 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 & & image . colours = = ZM_COLOUR_RGB32 ) {
Error ( " Overlay of RGB32 ontop of RGB24 is not supported. " ) ;
2017-04-28 22:34:48 +08:00
/* Grayscale ontop of RGB32 - complete */
2016-04-04 22:11:48 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 & & image . colours = = ZM_COLOUR_GRAY8 ) {
const Rgb * const max_ptr = ( Rgb * ) ( buffer + size ) ;
Rgb * prdest = ( Rgb * ) buffer ;
const uint8_t * psrc = image . buffer ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( subpixelorder = = ZM_SUBPIX_ORDER_RGBA | | subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
2016-04-04 22:11:48 +08:00
/* RGBA\BGRA subpixel order - Alpha byte is last */
2018-10-22 22:41:27 +08:00
while ( prdest < max_ptr ) {
if ( * psrc ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( prdest ) = GREEN_PTR_RGBA ( prdest ) = BLUE_PTR_RGBA ( prdest ) = * psrc ;
}
prdest + + ;
psrc + + ;
}
} else {
/* ABGR\ARGB subpixel order - Alpha byte is first */
2018-10-22 22:41:27 +08:00
while ( prdest < max_ptr ) {
if ( * psrc ) {
2016-04-04 22:11:48 +08:00
RED_PTR_ABGR ( prdest ) = GREEN_PTR_ABGR ( prdest ) = BLUE_PTR_ABGR ( prdest ) = * psrc ;
}
prdest + + ;
psrc + + ;
}
}
2017-04-28 22:34:48 +08:00
/* RGB24 ontop of RGB32 - TO BE DONE */
2016-04-04 22:11:48 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 & & image . colours = = ZM_COLOUR_RGB24 ) {
Error ( " Overlay of RGB24 ontop of RGB32 is not supported. " ) ;
2017-04-28 22:34:48 +08:00
/* RGB32 ontop of RGB32 - not complete. need to take care of different subpixel orders */
2016-04-04 22:11:48 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 & & image . colours = = ZM_COLOUR_RGB32 ) {
const Rgb * const max_ptr = ( Rgb * ) ( buffer + size ) ;
Rgb * prdest = ( Rgb * ) buffer ;
2019-09-17 22:31:48 +08:00
const Rgb * prsrc = ( Rgb * ) image . buffer ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( image . subpixelorder = = ZM_SUBPIX_ORDER_RGBA | | image . subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
2016-04-04 22:11:48 +08:00
/* RGB\BGR\RGBA\BGRA subpixel order - Alpha byte is last */
2018-10-22 22:41:27 +08:00
while ( prdest < max_ptr ) {
if ( RED_PTR_RGBA ( prsrc ) | | GREEN_PTR_RGBA ( prsrc ) | | BLUE_PTR_RGBA ( prsrc ) ) {
2016-04-04 22:11:48 +08:00
* prdest = * prsrc ;
}
prdest + + ;
prsrc + + ;
}
} else {
/* ABGR\ARGB subpixel order - Alpha byte is first */
2018-10-22 22:41:27 +08:00
while ( prdest < max_ptr ) {
if ( RED_PTR_ABGR ( prsrc ) | | GREEN_PTR_ABGR ( prsrc ) | | BLUE_PTR_ABGR ( prsrc ) ) {
2016-04-04 22:11:48 +08:00
* prdest = * prsrc ;
}
prdest + + ;
prsrc + + ;
}
}
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
}
/* RGB32 compatible: complete */
2018-10-22 22:41:27 +08:00
void Image : : Overlay ( const Image & image , unsigned int x , unsigned int y ) {
if ( ! ( width < image . width | | height < image . height ) ) {
Panic ( " Attempt to overlay image too big for destination, %dx%d > %dx%d " ,
image . width , image . height , width , height ) ;
2016-04-04 22:11:48 +08:00
}
2018-10-22 22:41:27 +08:00
if ( ! ( width < ( x + image . width ) | | height < ( y + image . height ) ) ) {
Panic ( " Attempt to overlay image outside of destination bounds, %dx%d @ %dx%d > %dx%d " ,
image . width , image . height , x , y , width , height ) ;
2016-04-04 22:11:48 +08:00
}
2018-10-22 22:41:27 +08:00
if ( ! ( colours = = image . colours ) ) {
Panic ( " Attempt to partial overlay differently coloured images, expected %d, got %d " ,
colours , image . colours ) ;
2016-04-04 22:11:48 +08:00
}
unsigned int lo_x = x ;
unsigned int lo_y = y ;
unsigned int hi_x = ( x + image . width ) - 1 ;
unsigned int hi_y = ( y + image . height - 1 ) ;
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
2016-04-04 22:11:48 +08:00
const uint8_t * psrc = image . buffer ;
2018-10-22 22:41:27 +08:00
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
uint8_t * pdest = & buffer [ ( y * width ) + lo_x ] ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = hi_x ; x + + ) {
2016-04-04 22:11:48 +08:00
* pdest + + = * psrc + + ;
}
2013-03-17 07:45:21 +08:00
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
2016-04-04 22:11:48 +08:00
const uint8_t * psrc = image . buffer ;
2018-10-22 22:41:27 +08:00
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
uint8_t * pdest = & buffer [ colours * ( ( y * width ) + lo_x ) ] ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = hi_x ; x + + ) {
2016-04-04 22:11:48 +08:00
* pdest + + = * psrc + + ;
* pdest + + = * psrc + + ;
* pdest + + = * psrc + + ;
}
2013-03-17 07:45:21 +08:00
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
2016-04-04 22:11:48 +08:00
const Rgb * psrc = ( Rgb * ) ( image . buffer ) ;
2018-10-22 22:41:27 +08:00
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
Rgb * pdest = ( Rgb * ) & buffer [ ( ( y * width ) + lo_x ) < < 2 ] ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = hi_x ; x + + ) {
2016-04-04 22:11:48 +08:00
* pdest + + = * psrc + + ;
}
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
} else {
Error ( " Overlay called with unexpected colours: %d " , colours ) ;
}
2020-07-08 04:13:41 +08:00
} // end void Image::Overlay( const Image &image, unsigned int x, unsigned int y )
2013-03-17 07:45:21 +08:00
2018-10-22 22:41:27 +08:00
void Image : : Blend ( const Image & image , int transparency ) {
2013-03-17 07:45:21 +08:00
# ifdef ZM_IMAGE_PROFILING
2016-04-04 22:11:48 +08:00
struct timespec start , end , diff ;
unsigned long long executetime ;
unsigned long milpixels ;
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
uint8_t * new_buffer ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( ! (
width = = image . width & & height = = image . height
& & colours = = image . colours
& & subpixelorder = = image . subpixelorder
) ) {
Panic ( " Attempt to blend different sized images, expected %dx%dx%d %d, got %dx%dx%d %d " ,
width , height , colours , subpixelorder , image . width , image . height , image . colours , image . subpixelorder ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( transparency < = 0 )
2016-04-04 22:11:48 +08:00
return ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
new_buffer = AllocBuffer ( size ) ;
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
# ifdef ZM_IMAGE_PROFILING
2016-04-04 22:11:48 +08:00
clock_gettime ( CLOCK_THREAD_CPUTIME_ID , & start ) ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Do the blending */
2019-02-22 22:44:57 +08:00
( * blend ) ( buffer , image . buffer , new_buffer , size , transparency ) ;
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
# ifdef ZM_IMAGE_PROFILING
2016-04-04 22:11:48 +08:00
clock_gettime ( CLOCK_THREAD_CPUTIME_ID , & end ) ;
timespec_diff ( & start , & end , & diff ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
executetime = ( 1000000000ull * diff . tv_sec ) + diff . tv_nsec ;
milpixels = ( unsigned long ) ( ( long double ) size ) / ( ( ( ( long double ) executetime ) / 1000 ) ) ;
Debug ( 5 , " Blend: %u colours blended in %llu nanoseconds, %lu million colours/s \n " , size , executetime , milpixels ) ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
AssignDirect ( width , height , colours , subpixelorder , new_buffer , size , ZM_BUFTYPE_ZM ) ;
2013-03-17 07:45:21 +08:00
}
2020-07-25 00:41:27 +08:00
Image * Image : : Merge ( unsigned int n_images , Image * images [ ] ) {
2018-04-12 23:29:35 +08:00
if ( n_images = = 1 ) return new Image ( * images [ 0 ] ) ;
2016-04-04 22:11:48 +08:00
unsigned int width = images [ 0 ] - > width ;
unsigned int height = images [ 0 ] - > height ;
unsigned int colours = images [ 0 ] - > colours ;
2018-04-12 23:29:35 +08:00
for ( unsigned int i = 1 ; i < n_images ; i + + ) {
if ( ! ( width = = images [ i ] - > width & & height = = images [ i ] - > height & & colours = = images [ i ] - > colours ) ) {
2018-10-22 22:41:27 +08:00
Panic ( " Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d " ,
width , height , colours , images [ i ] - > width , images [ i ] - > height , images [ i ] - > colours , i ) ;
2016-04-04 22:11:48 +08:00
}
}
2019-02-22 22:44:57 +08:00
Image * result = new Image ( width , height , images [ 0 ] - > colours , images [ 0 ] - > subpixelorder ) ;
2016-04-04 22:11:48 +08:00
unsigned int size = result - > size ;
2018-04-12 23:29:35 +08:00
for ( unsigned int i = 0 ; i < size ; i + + ) {
2016-04-04 22:11:48 +08:00
unsigned int total = 0 ;
uint8_t * pdest = result - > buffer ;
2018-04-12 23:29:35 +08:00
for ( unsigned int j = 0 ; j < n_images ; j + + ) {
2016-04-04 22:11:48 +08:00
uint8_t * psrc = images [ j ] - > buffer ;
total + = * psrc ;
psrc + + ;
}
* pdest = total / n_images ;
pdest + + ;
}
2018-04-12 23:29:35 +08:00
return result ;
2013-03-17 07:45:21 +08:00
}
2020-07-25 00:41:27 +08:00
Image * Image : : Merge ( unsigned int n_images , Image * images [ ] , double weight ) {
2018-04-12 23:29:35 +08:00
if ( n_images = = 1 ) return new Image ( * images [ 0 ] ) ;
2016-04-04 22:11:48 +08:00
unsigned int width = images [ 0 ] - > width ;
unsigned int height = images [ 0 ] - > height ;
unsigned int colours = images [ 0 ] - > colours ;
2018-04-12 23:29:35 +08:00
for ( unsigned int i = 1 ; i < n_images ; i + + ) {
if ( ! ( width = = images [ i ] - > width & & height = = images [ i ] - > height & & colours = = images [ i ] - > colours ) ) {
2018-10-22 22:41:27 +08:00
Panic ( " Attempt to merge different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d " ,
width , height , colours , images [ i ] - > width , images [ i ] - > height , images [ i ] - > colours , i ) ;
2016-04-04 22:11:48 +08:00
}
}
2020-07-25 00:41:27 +08:00
Image * result = new Image ( * images [ 0 ] ) ;
2016-04-04 22:11:48 +08:00
unsigned int size = result - > size ;
double factor = 1.0 * weight ;
2018-04-12 23:29:35 +08:00
for ( unsigned int i = 1 ; i < n_images ; i + + ) {
2016-04-04 22:11:48 +08:00
uint8_t * pdest = result - > buffer ;
uint8_t * psrc = images [ i ] - > buffer ;
2018-04-12 23:29:35 +08:00
for ( unsigned int j = 0 ; j < size ; j + + ) {
2016-04-04 22:11:48 +08:00
* pdest = ( uint8_t ) ( ( ( * pdest ) * ( 1.0 - factor ) ) + ( ( * psrc ) * factor ) ) ;
pdest + + ;
psrc + + ;
}
factor * = weight ;
}
2018-04-12 23:29:35 +08:00
return result ;
2013-03-17 07:45:21 +08:00
}
2018-10-22 22:41:27 +08:00
Image * Image : : Highlight ( unsigned int n_images , Image * images [ ] , const Rgb threshold , const Rgb ref_colour ) {
2018-04-12 23:29:35 +08:00
if ( n_images = = 1 ) return new Image ( * images [ 0 ] ) ;
2016-04-04 22:11:48 +08:00
unsigned int width = images [ 0 ] - > width ;
unsigned int height = images [ 0 ] - > height ;
unsigned int colours = images [ 0 ] - > colours ;
2018-04-12 23:29:35 +08:00
for ( unsigned int i = 1 ; i < n_images ; i + + ) {
if ( ! ( width = = images [ i ] - > width & & height = = images [ i ] - > height & & colours = = images [ i ] - > colours ) ) {
2018-10-22 22:41:27 +08:00
Panic ( " Attempt to highlight different sized images, expected %dx%dx%d, got %dx%dx%d, for image %d " ,
width , height , colours , images [ i ] - > width , images [ i ] - > height , images [ i ] - > colours , i ) ;
2016-04-04 22:11:48 +08:00
}
}
2020-07-25 00:41:27 +08:00
Image * result = new Image ( width , height , images [ 0 ] - > colours , images [ 0 ] - > subpixelorder ) ;
2016-04-04 22:11:48 +08:00
unsigned int size = result - > size ;
2018-04-12 23:29:35 +08:00
for ( unsigned int c = 0 ; c < colours ; c + + ) {
2017-05-11 01:16:08 +08:00
unsigned int ref_colour_rgb = RGB_VAL ( ref_colour , c ) ;
2018-04-12 23:29:35 +08:00
for ( unsigned int i = 0 ; i < size ; i + + ) {
2016-04-04 22:11:48 +08:00
unsigned int count = 0 ;
uint8_t * pdest = result - > buffer + c ;
2018-04-12 23:29:35 +08:00
for ( unsigned int j = 0 ; j < n_images ; j + + ) {
2016-04-04 22:11:48 +08:00
uint8_t * psrc = images [ j ] - > buffer + c ;
2013-03-17 07:45:21 +08:00
2017-05-11 01:16:08 +08:00
unsigned int diff = ( ( * psrc ) - ref_colour_rgb ) > 0 ? ( * psrc ) - ref_colour_rgb : ref_colour_rgb - ( * psrc ) ;
2017-01-31 15:42:57 +08:00
2018-04-12 23:29:35 +08:00
if ( diff > = RGB_VAL ( threshold , c ) ) {
2016-04-04 22:11:48 +08:00
count + + ;
}
psrc + = colours ;
}
* pdest = ( count * 255 ) / n_images ;
pdest + = 3 ;
}
}
2018-10-22 22:41:27 +08:00
return result ;
2013-03-17 07:45:21 +08:00
}
2015-04-19 18:38:23 +08:00
/* New function to allow buffer re-using instead of allocationg memory for the delta image every time */
2019-02-26 22:38:26 +08:00
void Image : : Delta ( const Image & image , Image * targetimage ) const {
2013-03-17 07:45:21 +08:00
# ifdef ZM_IMAGE_PROFILING
2016-04-04 22:11:48 +08:00
struct timespec start , end , diff ;
unsigned long long executetime ;
unsigned long milpixels ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( ! ( width = = image . width & & height = = image . height & & colours = = image . colours & & subpixelorder = = image . subpixelorder ) ) {
Panic ( " Attempt to get delta of different sized images, expected %dx%dx%d %d, got %dx%dx%d %d " ,
width , height , colours , subpixelorder , image . width , image . height , image . colours , image . subpixelorder ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
uint8_t * pdiff = targetimage - > WriteBuffer ( width , height , ZM_COLOUR_GRAY8 , ZM_SUBPIX_ORDER_NONE ) ;
2017-04-28 22:34:48 +08:00
2020-08-26 07:45:48 +08:00
if ( pdiff = = nullptr ) {
2016-04-04 22:11:48 +08:00
Panic ( " Failed requesting writeable buffer for storing the delta image " ) ;
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
# ifdef ZM_IMAGE_PROFILING
2016-04-04 22:11:48 +08:00
clock_gettime ( CLOCK_THREAD_CPUTIME_ID , & start ) ;
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
switch ( colours ) {
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB24 :
2019-02-22 22:44:57 +08:00
if ( subpixelorder = = ZM_SUBPIX_ORDER_BGR ) {
/* BGR subpixel order */
( * delta8_bgr ) ( buffer , image . buffer , pdiff , pixels ) ;
} else {
/* Assume RGB subpixel order */
( * delta8_rgb ) ( buffer , image . buffer , pdiff , pixels ) ;
2017-04-28 22:34:48 +08:00
}
2019-02-22 22:44:57 +08:00
break ;
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB32 :
2019-02-22 22:44:57 +08:00
if ( subpixelorder = = ZM_SUBPIX_ORDER_ARGB ) {
/* ARGB subpixel order */
( * delta8_argb ) ( buffer , image . buffer , pdiff , pixels ) ;
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_ABGR ) {
/* ABGR subpixel order */
( * delta8_abgr ) ( buffer , image . buffer , pdiff , pixels ) ;
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
/* BGRA subpixel order */
( * delta8_bgra ) ( buffer , image . buffer , pdiff , pixels ) ;
} else {
/* Assume RGBA subpixel order */
( * delta8_rgba ) ( buffer , image . buffer , pdiff , pixels ) ;
2017-04-28 22:34:48 +08:00
}
2019-02-22 22:44:57 +08:00
break ;
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_GRAY8 :
2019-02-22 22:44:57 +08:00
( * delta8_gray8 ) ( buffer , image . buffer , pdiff , pixels ) ;
2017-04-28 22:34:48 +08:00
break ;
2016-04-04 22:11:48 +08:00
default :
2017-04-28 22:34:48 +08:00
Panic ( " Delta called with unexpected colours: %d " , colours ) ;
break ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
# ifdef ZM_IMAGE_PROFILING
2016-04-04 22:11:48 +08:00
clock_gettime ( CLOCK_THREAD_CPUTIME_ID , & end ) ;
timespec_diff ( & start , & end , & diff ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
executetime = ( 1000000000ull * diff . tv_sec ) + diff . tv_nsec ;
milpixels = ( unsigned long ) ( ( long double ) pixels ) / ( ( ( ( long double ) executetime ) / 1000 ) ) ;
2019-02-22 22:44:57 +08:00
Debug ( 5 , " Delta: %u delta pixels generated in %llu nanoseconds, %lu million pixels/s " , pixels , executetime , milpixels ) ;
2013-03-17 07:45:21 +08:00
# endif
}
2020-10-06 01:15:39 +08:00
const Coord Image : : centreCoord ( const char * text , int size = 1 ) const {
2016-04-04 22:11:48 +08:00
int index = 0 ;
int line_no = 0 ;
2020-10-06 01:15:39 +08:00
int text_len = strlen ( text ) ;
2016-04-04 22:11:48 +08:00
int line_len = 0 ;
int max_line_len = 0 ;
const char * line = text ;
2017-11-19 05:00:10 +08:00
while ( ( index < text_len ) & & ( line_len = strcspn ( line , " \n " ) ) ) {
2016-04-04 22:11:48 +08:00
if ( line_len > max_line_len )
max_line_len = line_len ;
index + = line_len ;
2017-11-19 05:00:10 +08:00
while ( text [ index ] = = ' \n ' ) {
2016-04-04 22:11:48 +08:00
index + + ;
}
line = text + index ;
line_no + + ;
}
2020-11-28 21:16:20 +08:00
2020-12-04 07:31:41 +08:00
font . SetFontSize ( size - 1 ) ;
2020-11-29 12:55:06 +08:00
uint16_t char_width = font . GetCharWidth ( ) ;
uint16_t char_height = font . GetCharHeight ( ) ;
2020-11-28 21:16:20 +08:00
int x = ( width - ( max_line_len * char_width ) ) / 2 ;
int y = ( height - ( line_no * char_height ) ) / 2 ;
2020-07-08 04:13:41 +08:00
return Coord ( x , y ) ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
/* RGB32 compatible: complete */
2017-11-19 05:00:10 +08:00
void Image : : MaskPrivacy ( const unsigned char * p_bitmask , const Rgb pixel_colour ) {
2016-04-04 22:11:48 +08:00
const uint8_t pixel_r_col = RED_VAL_RGBA ( pixel_colour ) ;
const uint8_t pixel_g_col = GREEN_VAL_RGBA ( pixel_colour ) ;
const uint8_t pixel_b_col = BLUE_VAL_RGBA ( pixel_colour ) ;
const uint8_t pixel_bw_col = pixel_colour & 0xff ;
const Rgb pixel_rgb_col = rgb_convert ( pixel_colour , subpixelorder ) ;
unsigned char * ptr = & buffer [ 0 ] ;
unsigned int i = 0 ;
2018-10-22 22:41:27 +08:00
for ( unsigned int y = 0 ; y < height ; y + + ) {
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int x = 0 ; x < width ; x + + , ptr + + ) {
2016-04-04 22:11:48 +08:00
if ( p_bitmask [ i ] )
* ptr = pixel_bw_col ;
i + + ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
for ( unsigned int x = 0 ; x < width ; x + + , ptr + = colours ) {
if ( p_bitmask [ i ] ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( ptr ) = pixel_r_col ;
GREEN_PTR_RGBA ( ptr ) = pixel_g_col ;
BLUE_PTR_RGBA ( ptr ) = pixel_b_col ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
i + + ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
for ( unsigned int x = 0 ; x < width ; x + + , ptr + = colours ) {
2016-04-04 22:11:48 +08:00
Rgb * temp_ptr = ( Rgb * ) ptr ;
if ( p_bitmask [ i ] )
* temp_ptr = pixel_rgb_col ;
i + + ;
}
2017-04-28 22:34:48 +08:00
} else {
Panic ( " MaskPrivacy called with unexpected colours: %d " , colours ) ;
return ;
}
2018-10-22 22:41:27 +08:00
} // end foreach y
2013-03-17 07:45:21 +08:00
}
2015-08-20 21:58:13 +08:00
/* RGB32 compatible: complete */
2020-11-28 21:16:20 +08:00
/* Bitmap decoding trick has been adopted from here:
https : //lemire.me/blog/2018/02/21/iterating-over-set-bits-quickly/
*/
2020-07-23 01:41:27 +08:00
void Image : : Annotate (
const char * p_text ,
const Coord & coord ,
const unsigned int size ,
const Rgb fg_colour ,
const Rgb bg_colour ) {
2020-05-29 23:44:34 +08:00
strncpy ( text , p_text , sizeof ( text ) - 1 ) ;
2016-04-04 22:11:48 +08:00
unsigned int index = 0 ;
unsigned int line_no = 0 ;
2020-05-29 23:44:34 +08:00
unsigned int text_len = strlen ( text ) ;
2016-04-04 22:11:48 +08:00
unsigned int line_len = 0 ;
const char * line = text ;
const uint8_t fg_r_col = RED_VAL_RGBA ( fg_colour ) ;
const uint8_t fg_g_col = GREEN_VAL_RGBA ( fg_colour ) ;
const uint8_t fg_b_col = BLUE_VAL_RGBA ( fg_colour ) ;
const uint8_t fg_bw_col = fg_colour & 0xff ;
2020-11-29 12:55:06 +08:00
const Rgb fg_rgb_col = rgb_convert ( fg_colour , subpixelorder ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
const uint8_t bg_r_col = RED_VAL_RGBA ( bg_colour ) ;
const uint8_t bg_g_col = GREEN_VAL_RGBA ( bg_colour ) ;
const uint8_t bg_b_col = BLUE_VAL_RGBA ( bg_colour ) ;
const uint8_t bg_bw_col = bg_colour & 0xff ;
2020-11-29 12:55:06 +08:00
const Rgb bg_rgb_col = rgb_convert ( bg_colour , subpixelorder ) ;
2021-03-01 07:13:14 +08:00
const bool bg_trans = ( bg_colour = = kRGBTransparent ) ;
2016-04-04 22:11:48 +08:00
2020-12-04 07:31:41 +08:00
font . SetFontSize ( size - 1 ) ;
2020-11-29 12:55:06 +08:00
const uint16_t char_width = font . GetCharWidth ( ) ;
const uint16_t char_height = font . GetCharHeight ( ) ;
const uint64_t * font_bitmap = font . GetBitmapData ( ) ;
2020-12-16 04:59:22 +08:00
Debug ( 4 , " Font size %d, char_width %d char_height %d " , size , char_width , char_height ) ;
2016-04-04 22:11:48 +08:00
2020-05-29 23:44:34 +08:00
while ( ( index < text_len ) & & ( line_len = strcspn ( line , " \n " ) ) ) {
2020-11-28 21:16:20 +08:00
unsigned int line_width = line_len * char_width ;
2016-04-04 22:11:48 +08:00
unsigned int lo_line_x = coord . X ( ) ;
2020-11-28 21:16:20 +08:00
unsigned int lo_line_y = coord . Y ( ) + ( line_no * char_height ) ;
2016-04-04 22:11:48 +08:00
unsigned int min_line_x = 0 ;
2020-12-04 07:31:41 +08:00
// FIXME What if line_width > width?
2016-04-04 22:11:48 +08:00
unsigned int max_line_x = width - line_width ;
2020-12-04 05:43:27 +08:00
unsigned int min_line_y = 0 ;
2020-11-28 21:16:20 +08:00
unsigned int max_line_y = height - char_height ;
2016-04-04 22:11:48 +08:00
if ( lo_line_x > max_line_x )
lo_line_x = max_line_x ;
if ( lo_line_x < min_line_x )
lo_line_x = min_line_x ;
if ( lo_line_y > max_line_y )
lo_line_y = max_line_y ;
if ( lo_line_y < min_line_y )
lo_line_y = min_line_y ;
unsigned int hi_line_x = lo_line_x + line_width ;
2020-11-28 21:16:20 +08:00
unsigned int hi_line_y = lo_line_y + char_height ;
2016-04-04 22:11:48 +08:00
// Clip anything that runs off the right of the screen
if ( hi_line_x > width )
hi_line_x = width ;
if ( hi_line_y > height )
hi_line_y = height ;
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
2016-04-04 22:11:48 +08:00
unsigned char * ptr = & buffer [ ( lo_line_y * width ) + lo_line_x ] ;
2020-11-28 21:16:20 +08:00
for ( unsigned int y = lo_line_y , r = 0 ; y < hi_line_y & & r < char_height ; y + + , r + + , ptr + = width ) {
2016-04-04 22:11:48 +08:00
unsigned char * temp_ptr = ptr ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_line_x , c = 0 ; x < hi_line_x & & c < line_len ; c + + ) {
2020-11-28 21:16:20 +08:00
if ( line [ c ] > 0xFF ) {
2020-12-04 05:43:27 +08:00
Warning ( " Unsupported character %c in %s " , line [ c ] , line ) ;
continue ;
2020-05-29 23:44:34 +08:00
}
2020-11-29 12:55:06 +08:00
uint64_t f = font_bitmap [ ( line [ c ] * char_height ) + r ] ;
if ( ! bg_trans ) memset ( temp_ptr , bg_bw_col , char_width ) ;
while ( f ! = 0 ) {
uint64_t t = f & - f ;
2021-01-15 14:16:30 +08:00
int idx = char_width - __builtin_ctzll ( f ) + size ;
2020-11-28 21:16:20 +08:00
* ( temp_ptr + idx ) = fg_bw_col ;
f ^ = t ;
2016-04-04 22:11:48 +08:00
}
2020-11-28 21:16:20 +08:00
temp_ptr + = char_width ;
2015-08-20 21:58:13 +08:00
}
2016-04-04 22:11:48 +08:00
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
2016-04-04 22:11:48 +08:00
unsigned int wc = width * colours ;
unsigned char * ptr = & buffer [ ( ( lo_line_y * width ) + lo_line_x ) * colours ] ;
2020-11-28 21:16:20 +08:00
for ( unsigned int y = lo_line_y , r = 0 ; y < hi_line_y & & r < char_height ; y + + , r + + , ptr + = wc ) {
2016-04-04 22:11:48 +08:00
unsigned char * temp_ptr = ptr ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_line_x , c = 0 ; x < hi_line_x & & c < line_len ; c + + ) {
2020-12-04 05:43:27 +08:00
if ( line [ c ] > 0xFF ) {
Warning ( " Unsupported character %c in %s " , line [ c ] , line ) ;
continue ;
2020-05-29 23:44:34 +08:00
}
2020-12-04 05:43:27 +08:00
uint64_t f = font_bitmap [ ( line [ c ] * char_height ) + r ] ;
if ( ! bg_trans ) {
for ( int i = 0 ; i < char_width ; i + + ) { // We need to set individual r,g,b components
unsigned char * colour_ptr = temp_ptr + ( i * 3 ) ;
RED_PTR_RGBA ( colour_ptr ) = bg_r_col ;
GREEN_PTR_RGBA ( colour_ptr ) = bg_g_col ;
BLUE_PTR_RGBA ( colour_ptr ) = bg_b_col ;
2016-04-04 22:11:48 +08:00
}
}
2020-12-04 05:43:27 +08:00
while ( f ! = 0 ) {
uint64_t t = f & - f ;
2021-01-15 14:16:30 +08:00
int idx = char_width - __builtin_ctzll ( f ) + size ;
2020-12-04 05:43:27 +08:00
unsigned char * colour_ptr = temp_ptr + ( idx * 3 ) ;
RED_PTR_RGBA ( colour_ptr ) = fg_r_col ;
GREEN_PTR_RGBA ( colour_ptr ) = fg_g_col ;
BLUE_PTR_RGBA ( colour_ptr ) = fg_b_col ;
f ^ = t ;
}
temp_ptr + = char_width * colours ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
2016-04-04 22:11:48 +08:00
unsigned int wc = width * colours ;
2020-11-28 21:16:20 +08:00
uint8_t * ptr = & buffer [ ( ( lo_line_y * width ) + lo_line_x ) < < 2 ] ;
for ( unsigned int y = lo_line_y , r = 0 ; y < hi_line_y & & r < char_height ; y + + , r + + , ptr + = wc ) {
2016-04-04 22:11:48 +08:00
Rgb * temp_ptr = ( Rgb * ) ptr ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_line_x , c = 0 ; x < hi_line_x & & c < line_len ; c + + ) {
2020-11-28 21:16:20 +08:00
if ( line [ c ] > 0xFF ) {
2020-12-04 05:43:27 +08:00
Warning ( " Unsupported character %c in %s " , line [ c ] , line ) ;
continue ;
2020-05-29 23:44:34 +08:00
}
2020-12-04 05:43:27 +08:00
uint64_t f = font_bitmap [ ( line [ c ] * char_height ) + r ] ;
if ( ! bg_trans ) {
for ( int i = 0 ; i < char_width ; i + + )
* ( temp_ptr + i ) = bg_rgb_col ;
2016-04-04 22:11:48 +08:00
}
2020-12-04 05:43:27 +08:00
while ( f ! = 0 ) {
uint64_t t = f & - f ;
2021-01-15 14:16:30 +08:00
int idx = char_width - __builtin_ctzll ( f ) + size ;
2020-12-04 05:43:27 +08:00
* ( temp_ptr + idx ) = fg_rgb_col ;
f ^ = t ;
2016-04-04 22:11:48 +08:00
}
2020-12-04 05:43:27 +08:00
temp_ptr + = char_width ;
2013-03-17 07:45:21 +08:00
}
2019-09-17 22:31:48 +08:00
}
2017-04-28 22:34:48 +08:00
} else {
2020-07-23 01:41:27 +08:00
Error ( " Annotate called with unexpected colours: %d " , colours ) ;
2017-04-28 22:34:48 +08:00
return ;
}
2016-04-04 22:11:48 +08:00
index + = line_len ;
2018-10-22 22:41:27 +08:00
while ( text [ index ] = = ' \n ' ) {
2016-04-04 22:11:48 +08:00
index + + ;
2013-03-17 07:45:21 +08:00
}
2016-04-04 22:11:48 +08:00
line = text + index ;
line_no + + ;
}
2013-03-17 07:45:21 +08:00
}
2017-04-28 22:34:48 +08:00
void Image : : Timestamp ( const char * label , const time_t when , const Coord & coord , const int size ) {
2016-04-04 22:11:48 +08:00
char time_text [ 64 ] ;
2021-03-07 23:35:05 +08:00
tm when_tm = { } ;
strftime ( time_text , sizeof ( time_text ) , " %y/%m/%d %H:%M:%S " , localtime_r ( & when , & when_tm ) ) ;
2017-04-28 22:34:48 +08:00
if ( label ) {
2019-10-18 21:43:25 +08:00
// Assume label is max 64, + ' - ' + 64 chars of time_text
char text [ 132 ] ;
snprintf ( text , sizeof ( text ) , " %s - %s " , label , time_text ) ;
Annotate ( text , coord , size ) ;
2017-04-28 22:34:48 +08:00
} else {
2019-10-18 21:43:25 +08:00
Annotate ( time_text , coord , size ) ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/* RGB32 compatible: complete */
2018-10-22 22:41:27 +08:00
void Image : : Colourise ( const unsigned int p_reqcolours , const unsigned int p_reqsubpixelorder ) {
2016-04-04 22:11:48 +08:00
Debug ( 9 , " Colourise: Req colours: %u Req subpixel order: %u Current colours: %u Current subpixel order: %u " , p_reqcolours , p_reqsubpixelorder , colours , subpixelorder ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
if ( colours ! = ZM_COLOUR_GRAY8 ) {
Warning ( " Target image is already colourised, colours: %u " , colours ) ;
return ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
if ( p_reqcolours = = ZM_COLOUR_RGB32 ) {
/* RGB32 */
Rgb * new_buffer = ( Rgb * ) AllocBuffer ( pixels * sizeof ( Rgb ) ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
const uint8_t * psrc = buffer ;
Rgb * pdest = new_buffer ;
Rgb subpixel ;
Rgb newpixel ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( p_reqsubpixelorder = = ZM_SUBPIX_ORDER_ABGR | | p_reqsubpixelorder = = ZM_SUBPIX_ORDER_ARGB ) {
2016-04-04 22:11:48 +08:00
/* ARGB\ABGR subpixel order. alpha byte is first (mem+0), so we need to shift the pixel left in the end */
2018-10-22 22:41:27 +08:00
for ( unsigned int i = 0 ; i < pixels ; i + + ) {
2016-04-04 22:11:48 +08:00
newpixel = subpixel = psrc [ i ] ;
newpixel = ( newpixel < < 8 ) | subpixel ;
newpixel = ( newpixel < < 8 ) | subpixel ;
pdest [ i ] = ( newpixel < < 8 ) ;
2019-09-17 22:31:48 +08:00
}
2016-04-04 22:11:48 +08:00
} else {
/* RGBA\BGRA subpixel order, alpha byte is last (mem+3) */
2018-10-22 22:41:27 +08:00
for ( unsigned int i = 0 ; i < pixels ; i + + ) {
2016-04-04 22:11:48 +08:00
newpixel = subpixel = psrc [ i ] ;
newpixel = ( newpixel < < 8 ) | subpixel ;
newpixel = ( newpixel < < 8 ) | subpixel ;
pdest [ i ] = newpixel ;
}
}
/* Directly assign the new buffer and make sure it will be freed when not needed anymore */
AssignDirect ( width , height , p_reqcolours , p_reqsubpixelorder , ( uint8_t * ) new_buffer , pixels * 4 , ZM_BUFTYPE_ZM ) ;
2018-10-22 22:41:27 +08:00
} else if ( p_reqcolours = = ZM_COLOUR_RGB24 ) {
2016-04-04 22:11:48 +08:00
/* RGB24 */
uint8_t * new_buffer = AllocBuffer ( pixels * 3 ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
uint8_t * pdest = new_buffer ;
const uint8_t * psrc = buffer ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
for ( unsigned int i = 0 ; i < ( unsigned int ) pixels ; i + + , pdest + = 3 ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( pdest ) = GREEN_PTR_RGBA ( pdest ) = BLUE_PTR_RGBA ( pdest ) = psrc [ i ] ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Directly assign the new buffer and make sure it will be freed when not needed anymore */
AssignDirect ( width , height , p_reqcolours , p_reqsubpixelorder , new_buffer , pixels * 3 , ZM_BUFTYPE_ZM ) ;
} else {
2018-10-22 22:41:27 +08:00
Error ( " Colourise called with unexpected colours: %d " , colours ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2013-03-17 07:45:21 +08:00
}
/* RGB32 compatible: complete */
2018-10-22 22:41:27 +08:00
void Image : : DeColourise ( ) {
2016-04-04 22:11:48 +08:00
colours = ZM_COLOUR_GRAY8 ;
subpixelorder = ZM_SUBPIX_ORDER_NONE ;
size = width * height ;
2017-04-28 22:34:48 +08:00
2020-07-20 05:50:47 +08:00
if ( colours = = ZM_COLOUR_RGB32 & & config . cpu_extensions & & sse_version > = 35 ) {
2019-09-17 22:31:48 +08:00
/* Use SSSE3 functions */
2019-02-22 22:44:57 +08:00
switch ( subpixelorder ) {
2016-04-04 22:11:48 +08:00
case ZM_SUBPIX_ORDER_BGRA :
2017-04-28 22:34:48 +08:00
ssse3_convert_bgra_gray8 ( buffer , buffer , pixels ) ;
break ;
2016-04-04 22:11:48 +08:00
case ZM_SUBPIX_ORDER_ARGB :
2017-04-28 22:34:48 +08:00
ssse3_convert_argb_gray8 ( buffer , buffer , pixels ) ;
break ;
2016-04-04 22:11:48 +08:00
case ZM_SUBPIX_ORDER_ABGR :
2017-04-28 22:34:48 +08:00
ssse3_convert_abgr_gray8 ( buffer , buffer , pixels ) ;
break ;
2016-04-04 22:11:48 +08:00
case ZM_SUBPIX_ORDER_RGBA :
default :
2017-04-28 22:34:48 +08:00
ssse3_convert_rgba_gray8 ( buffer , buffer , pixels ) ;
break ;
2016-04-04 22:11:48 +08:00
}
} else {
2017-03-15 04:29:55 +08:00
/* Use standard functions */
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_RGB32 ) {
2019-02-22 22:44:57 +08:00
if ( pixels % 16 ) {
switch ( subpixelorder ) {
case ZM_SUBPIX_ORDER_BGRA :
std_convert_bgra_gray8 ( buffer , buffer , pixels ) ;
break ;
case ZM_SUBPIX_ORDER_ARGB :
std_convert_argb_gray8 ( buffer , buffer , pixels ) ;
break ;
case ZM_SUBPIX_ORDER_ABGR :
std_convert_abgr_gray8 ( buffer , buffer , pixels ) ;
break ;
case ZM_SUBPIX_ORDER_RGBA :
default :
std_convert_rgba_gray8 ( buffer , buffer , pixels ) ;
break ;
}
} else {
switch ( subpixelorder ) {
case ZM_SUBPIX_ORDER_BGRA :
fast_convert_bgra_gray8 ( buffer , buffer , pixels ) ;
break ;
case ZM_SUBPIX_ORDER_ARGB :
fast_convert_argb_gray8 ( buffer , buffer , pixels ) ;
break ;
case ZM_SUBPIX_ORDER_ABGR :
fast_convert_abgr_gray8 ( buffer , buffer , pixels ) ;
break ;
case ZM_SUBPIX_ORDER_RGBA :
default :
fast_convert_rgba_gray8 ( buffer , buffer , pixels ) ;
break ;
}
} // end if pixels % 16 to use loop unrolled functions
2017-03-15 04:29:55 +08:00
} else {
/* Assume RGB24 */
2019-02-22 22:44:57 +08:00
if ( pixels % 12 ) {
switch ( subpixelorder ) {
case ZM_SUBPIX_ORDER_BGR :
std_convert_bgr_gray8 ( buffer , buffer , pixels ) ;
break ;
case ZM_SUBPIX_ORDER_RGB :
default :
std_convert_rgb_gray8 ( buffer , buffer , pixels ) ;
break ;
}
} else {
switch ( subpixelorder ) {
case ZM_SUBPIX_ORDER_BGR :
fast_convert_bgr_gray8 ( buffer , buffer , pixels ) ;
break ;
case ZM_SUBPIX_ORDER_RGB :
default :
fast_convert_rgb_gray8 ( buffer , buffer , pixels ) ;
break ;
}
} // end if pixels % 12 to use loop unrolled functions
}
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/* RGB32 compatible: complete */
2018-10-22 22:41:27 +08:00
void Image : : Fill ( Rgb colour , const Box * limits ) {
if ( ! ( colours = = ZM_COLOUR_GRAY8 | | colours = = ZM_COLOUR_RGB24 | | colours = = ZM_COLOUR_RGB32 ) ) {
2019-02-22 22:44:57 +08:00
Panic ( " Attempt to fill image with unexpected colours %d " , colours ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Convert the colour's RGBA subpixel order into the image's subpixel order */
colour = rgb_convert ( colour , subpixelorder ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
unsigned int lo_x = limits ? limits - > Lo ( ) . X ( ) : 0 ;
unsigned int lo_y = limits ? limits - > Lo ( ) . Y ( ) : 0 ;
unsigned int hi_x = limits ? limits - > Hi ( ) . X ( ) : width - 1 ;
unsigned int hi_y = limits ? limits - > Hi ( ) . Y ( ) : height - 1 ;
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
unsigned char * p = & buffer [ ( y * width ) + lo_x ] ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = hi_x ; x + + , p + + ) {
2016-04-04 22:11:48 +08:00
* p = colour ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
unsigned char * p = & buffer [ colours * ( ( y * width ) + lo_x ) ] ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = hi_x ; x + + , p + = 3 ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( p ) = RED_VAL_RGBA ( colour ) ;
GREEN_PTR_RGBA ( p ) = GREEN_VAL_RGBA ( colour ) ;
BLUE_PTR_RGBA ( p ) = BLUE_VAL_RGBA ( colour ) ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) /* RGB32 */ {
for ( unsigned int y = lo_y ; y < = ( unsigned int ) hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
Rgb * p = ( Rgb * ) & buffer [ ( ( y * width ) + lo_x ) < < 2 ] ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = ( unsigned int ) hi_x ; x + + , p + + ) {
2019-09-17 22:31:48 +08:00
/* Fast, copies the entire pixel in a single pass */
2016-04-04 22:11:48 +08:00
* p = colour ;
}
}
}
2013-03-17 07:45:21 +08:00
}
/* RGB32 compatible: complete */
2018-10-22 22:41:27 +08:00
void Image : : Fill ( Rgb colour , int density , const Box * limits ) {
2016-04-04 22:11:48 +08:00
/* Allow the faster version to be used if density is not used (density=1) */
2018-10-22 22:41:27 +08:00
if ( density < = 1 )
2016-04-04 22:11:48 +08:00
return Fill ( colour , limits ) ;
2017-04-28 22:34:48 +08:00
2020-07-08 04:13:41 +08:00
if ( ! ( colours = = ZM_COLOUR_GRAY8 | | colours = = ZM_COLOUR_RGB24 | | colours = = ZM_COLOUR_RGB32 ) ) {
2018-10-22 22:41:27 +08:00
Panic ( " Attempt to fill image with unexpected colours %d " , colours ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Convert the colour's RGBA subpixel order into the image's subpixel order */
2020-07-08 04:13:41 +08:00
colour = rgb_convert ( colour , subpixelorder ) ;
2016-04-04 22:11:48 +08:00
unsigned int lo_x = limits ? limits - > Lo ( ) . X ( ) : 0 ;
unsigned int lo_y = limits ? limits - > Lo ( ) . Y ( ) : 0 ;
unsigned int hi_x = limits ? limits - > Hi ( ) . X ( ) : width - 1 ;
unsigned int hi_y = limits ? limits - > Hi ( ) . Y ( ) : height - 1 ;
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
unsigned char * p = & buffer [ ( y * width ) + lo_x ] ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = hi_x ; x + + , p + + ) {
2016-04-04 22:11:48 +08:00
if ( ( x = = lo_x | | x = = hi_x | | y = = lo_y | | y = = hi_y ) | | ( ! ( x % density ) & & ! ( y % density ) ) )
* p = colour ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
unsigned char * p = & buffer [ colours * ( ( y * width ) + lo_x ) ] ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = hi_x ; x + + , p + = 3 ) {
2016-04-04 22:11:48 +08:00
if ( ( x = = lo_x | | x = = hi_x | | y = = lo_y | | y = = hi_y ) | | ( ! ( x % density ) & & ! ( y % density ) ) ) {
RED_PTR_RGBA ( p ) = RED_VAL_RGBA ( colour ) ;
GREEN_PTR_RGBA ( p ) = GREEN_VAL_RGBA ( colour ) ;
BLUE_PTR_RGBA ( p ) = BLUE_VAL_RGBA ( colour ) ;
}
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) /* RGB32 */ {
for ( unsigned int y = lo_y ; y < = hi_y ; y + + ) {
2016-04-04 22:11:48 +08:00
Rgb * p = ( Rgb * ) & buffer [ ( ( y * width ) + lo_x ) < < 2 ] ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = lo_x ; x < = hi_x ; x + + , p + + ) {
2016-04-04 22:11:48 +08:00
if ( ( x = = lo_x | | x = = hi_x | | y = = lo_y | | y = = hi_y ) | | ( ! ( x % density ) & & ! ( y % density ) ) )
/* Fast, copies the entire pixel in a single pass */
* p = colour ;
}
}
2019-09-17 22:31:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/* RGB32 compatible: complete */
2018-10-22 22:41:27 +08:00
void Image : : Outline ( Rgb colour , const Polygon & polygon ) {
if ( ! ( colours = = ZM_COLOUR_GRAY8 | | colours = = ZM_COLOUR_RGB24 | | colours = = ZM_COLOUR_RGB32 ) ) {
Panic ( " Attempt to outline image with unexpected colours %d " , colours ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Convert the colour's RGBA subpixel order into the image's subpixel order */
colour = rgb_convert ( colour , subpixelorder ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
int n_coords = polygon . getNumCoords ( ) ;
2018-10-22 22:41:27 +08:00
for ( int j = 0 , i = n_coords - 1 ; j < n_coords ; i = j + + ) {
2016-04-04 22:11:48 +08:00
const Coord & p1 = polygon . getCoord ( i ) ;
const Coord & p2 = polygon . getCoord ( j ) ;
int x1 = p1 . X ( ) ;
int x2 = p2 . X ( ) ;
int y1 = p1 . Y ( ) ;
int y2 = p2 . Y ( ) ;
double dx = x2 - x1 ;
double dy = y2 - y1 ;
double grad ;
2018-10-22 22:41:27 +08:00
if ( fabs ( dx ) < = fabs ( dy ) ) {
2016-04-04 22:11:48 +08:00
if ( y1 ! = y2 )
grad = dx / dy ;
else
grad = width ;
double x ;
int y , yinc = ( y1 < y2 ) ? 1 : - 1 ;
grad * = yinc ;
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( x = x1 , y = y1 ; y ! = y2 ; y + = yinc , x + = grad ) {
2016-04-04 22:11:48 +08:00
buffer [ ( y * width ) + int ( round ( x ) ) ] = colour ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
for ( x = x1 , y = y1 ; y ! = y2 ; y + = yinc , x + = grad ) {
2016-04-04 22:11:48 +08:00
unsigned char * p = & buffer [ colours * ( ( y * width ) + int ( round ( x ) ) ) ] ;
RED_PTR_RGBA ( p ) = RED_VAL_RGBA ( colour ) ;
GREEN_PTR_RGBA ( p ) = GREEN_VAL_RGBA ( colour ) ;
BLUE_PTR_RGBA ( p ) = BLUE_VAL_RGBA ( colour ) ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
for ( x = x1 , y = y1 ; y ! = y2 ; y + = yinc , x + = grad ) {
2016-04-04 22:11:48 +08:00
* ( Rgb * ) ( buffer + ( ( ( y * width ) + int ( round ( x ) ) ) < < 2 ) ) = colour ;
}
}
2018-10-22 22:41:27 +08:00
} else {
2016-04-04 22:11:48 +08:00
//Debug( 9, "dx > dy" );
if ( x1 ! = x2 )
grad = dy / dx ;
else
grad = height ;
//Debug( 9, "grad: %.2lf", grad );
double y ;
int x , xinc = ( x1 < x2 ) ? 1 : - 1 ;
grad * = xinc ;
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
2016-04-04 22:11:48 +08:00
//Debug( 9, "x1:%d, x2:%d, y1:%d, y2:%d, gr:%.2lf", x1, x2, y1, y2, grad );
2018-10-22 22:41:27 +08:00
for ( y = y1 , x = x1 ; x ! = x2 ; x + = xinc , y + = grad ) {
2016-04-04 22:11:48 +08:00
//Debug( 9, "x:%d, y:%.2f", x, y );
buffer [ ( int ( round ( y ) ) * width ) + x ] = colour ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
for ( y = y1 , x = x1 ; x ! = x2 ; x + = xinc , y + = grad ) {
2016-04-04 22:11:48 +08:00
unsigned char * p = & buffer [ colours * ( ( int ( round ( y ) ) * width ) + x ) ] ;
RED_PTR_RGBA ( p ) = RED_VAL_RGBA ( colour ) ;
GREEN_PTR_RGBA ( p ) = GREEN_VAL_RGBA ( colour ) ;
BLUE_PTR_RGBA ( p ) = BLUE_VAL_RGBA ( colour ) ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
for ( y = y1 , x = x1 ; x ! = x2 ; x + = xinc , y + = grad ) {
2016-04-04 22:11:48 +08:00
* ( Rgb * ) ( buffer + ( ( ( int ( round ( y ) ) * width ) + x ) < < 2 ) ) = colour ;
}
}
}
2018-10-22 22:41:27 +08:00
} // end foreach coordinate in the polygon
2013-03-17 07:45:21 +08:00
}
/* RGB32 compatible: complete */
2020-07-08 04:13:41 +08:00
void Image : : Fill ( Rgb colour , int density , const Polygon & polygon ) {
2018-10-22 22:41:27 +08:00
if ( ! ( colours = = ZM_COLOUR_GRAY8 | | colours = = ZM_COLOUR_RGB24 | | colours = = ZM_COLOUR_RGB32 ) ) {
2020-07-08 04:13:41 +08:00
Panic ( " Attempt to fill image with unexpected colours %d " , colours ) ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Convert the colour's RGBA subpixel order into the image's subpixel order */
2020-07-08 04:13:41 +08:00
colour = rgb_convert ( colour , subpixelorder ) ;
2016-04-04 22:11:48 +08:00
int n_coords = polygon . getNumCoords ( ) ;
int n_global_edges = 0 ;
Edge global_edges [ n_coords ] ;
2018-10-22 22:41:27 +08:00
for ( int j = 0 , i = n_coords - 1 ; j < n_coords ; i = j + + ) {
2020-07-08 04:13:41 +08:00
const Coord & p1 = polygon . getCoord ( i ) ;
const Coord & p2 = polygon . getCoord ( j ) ;
2016-04-04 22:11:48 +08:00
int x1 = p1 . X ( ) ;
int x2 = p2 . X ( ) ;
int y1 = p1 . Y ( ) ;
int y2 = p2 . Y ( ) ;
//Debug( 9, "x1:%d,y1:%d x2:%d,y2:%d", x1, y1, x2, y2 );
if ( y1 = = y2 )
continue ;
double dx = x2 - x1 ;
double dy = y2 - y1 ;
global_edges [ n_global_edges ] . min_y = y1 < y2 ? y1 : y2 ;
global_edges [ n_global_edges ] . max_y = y1 < y2 ? y2 : y1 ;
global_edges [ n_global_edges ] . min_x = y1 < y2 ? x1 : x2 ;
global_edges [ n_global_edges ] . _1_m = dx / dy ;
n_global_edges + + ;
}
2021-02-06 22:41:56 +08:00
std : : sort ( global_edges , global_edges + n_global_edges , Edge : : CompareYX ) ;
2013-03-17 07:45:21 +08:00
# ifndef ZM_DBG_OFF
2018-10-22 22:41:27 +08:00
if ( logLevel ( ) > = Logger : : DEBUG9 ) {
for ( int i = 0 ; i < n_global_edges ; i + + ) {
2020-07-25 03:26:28 +08:00
Debug ( 9 , " %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f " ,
i , global_edges [ i ] . min_y , global_edges [ i ] . max_y , global_edges [ i ] . min_x , global_edges [ i ] . _1_m ) ;
2016-04-04 22:11:48 +08:00
}
}
2013-03-17 07:45:21 +08:00
# endif
2016-04-04 22:11:48 +08:00
int n_active_edges = 0 ;
Edge active_edges [ n_global_edges ] ;
int y = global_edges [ 0 ] . min_y ;
2018-10-22 22:41:27 +08:00
do {
for ( int i = 0 ; i < n_global_edges ; i + + ) {
if ( global_edges [ i ] . min_y = = y ) {
Debug ( 9 , " Moving global edge " ) ;
2016-04-04 22:11:48 +08:00
active_edges [ n_active_edges + + ] = global_edges [ i ] ;
2018-10-22 22:41:27 +08:00
if ( i < ( n_global_edges - 1 ) ) {
2016-04-04 22:11:48 +08:00
//memcpy( &global_edges[i], &global_edges[i+1], sizeof(*global_edges)*(n_global_edges-i) );
memmove ( & global_edges [ i ] , & global_edges [ i + 1 ] , sizeof ( * global_edges ) * ( n_global_edges - i ) ) ;
i - - ;
}
n_global_edges - - ;
2018-10-22 22:41:27 +08:00
} else {
2016-04-04 22:11:48 +08:00
break ;
}
}
2021-02-06 22:41:56 +08:00
std : : sort ( active_edges , active_edges + n_active_edges , Edge : : CompareX ) ;
2013-03-17 07:45:21 +08:00
# ifndef ZM_DBG_OFF
2018-10-22 22:41:27 +08:00
if ( logLevel ( ) > = Logger : : DEBUG9 ) {
for ( int i = 0 ; i < n_active_edges ; i + + ) {
2020-07-25 03:26:28 +08:00
Debug ( 9 , " %d - %d: min_y: %d, max_y:%d, min_x:%.2f, 1/m:%.2f " ,
y , i , active_edges [ i ] . min_y , active_edges [ i ] . max_y , active_edges [ i ] . min_x , active_edges [ i ] . _1_m ) ;
2016-04-04 22:11:48 +08:00
}
}
2013-03-17 07:45:21 +08:00
# endif
2018-10-22 22:41:27 +08:00
if ( ! ( y % density ) ) {
2016-04-04 22:11:48 +08:00
//Debug( 9, "%d", y );
2018-10-22 22:41:27 +08:00
for ( int i = 0 ; i < n_active_edges ; ) {
2016-04-04 22:11:48 +08:00
int lo_x = int ( round ( active_edges [ i + + ] . min_x ) ) ;
int hi_x = int ( round ( active_edges [ i + + ] . min_x ) ) ;
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
2016-04-04 22:11:48 +08:00
unsigned char * p = & buffer [ ( y * width ) + lo_x ] ;
2018-10-22 22:41:27 +08:00
for ( int x = lo_x ; x < = hi_x ; x + + , p + + ) {
if ( ! ( x % density ) ) {
2016-04-04 22:11:48 +08:00
//Debug( 9, " %d", x );
* p = colour ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
2016-04-04 22:11:48 +08:00
unsigned char * p = & buffer [ colours * ( ( y * width ) + lo_x ) ] ;
2018-10-22 22:41:27 +08:00
for ( int x = lo_x ; x < = hi_x ; x + + , p + = 3 ) {
2019-09-17 22:31:48 +08:00
if ( ! ( x % density ) ) {
2016-04-04 22:11:48 +08:00
RED_PTR_RGBA ( p ) = RED_VAL_RGBA ( colour ) ;
GREEN_PTR_RGBA ( p ) = GREEN_VAL_RGBA ( colour ) ;
BLUE_PTR_RGBA ( p ) = BLUE_VAL_RGBA ( colour ) ;
}
}
} else if ( colours = = ZM_COLOUR_RGB32 ) {
Rgb * p = ( Rgb * ) & buffer [ ( ( y * width ) + lo_x ) < < 2 ] ;
2018-10-22 22:41:27 +08:00
for ( int x = lo_x ; x < = hi_x ; x + + , p + + ) {
if ( ! ( x % density ) ) {
2016-04-04 22:11:48 +08:00
/* Fast, copies the entire pixel in a single pass */
* p = colour ;
}
}
}
}
}
y + + ;
2018-10-22 22:41:27 +08:00
for ( int i = n_active_edges - 1 ; i > = 0 ; i - - ) {
if ( y > = active_edges [ i ] . max_y ) {
// Or >= as per sheets
2020-07-25 03:26:28 +08:00
Debug ( 9 , " Deleting active_edge " ) ;
2018-10-22 22:41:27 +08:00
if ( i < ( n_active_edges - 1 ) ) {
2016-04-04 22:11:48 +08:00
//memcpy( &active_edges[i], &active_edges[i+1], sizeof(*active_edges)*(n_active_edges-i) );
memmove ( & active_edges [ i ] , & active_edges [ i + 1 ] , sizeof ( * active_edges ) * ( n_active_edges - i ) ) ;
}
n_active_edges - - ;
2018-10-22 22:41:27 +08:00
} else {
2016-04-04 22:11:48 +08:00
active_edges [ i ] . min_x + = active_edges [ i ] . _1_m ;
}
}
} while ( n_global_edges | | n_active_edges ) ;
2013-03-17 07:45:21 +08:00
}
2020-07-25 03:26:28 +08:00
void Image : : Fill ( Rgb colour , const Polygon & polygon ) {
Fill ( colour , 1 , polygon ) ;
2013-03-17 07:45:21 +08:00
}
2020-07-25 00:41:27 +08:00
void Image : : Rotate ( int angle ) {
2016-04-04 22:11:48 +08:00
angle % = 360 ;
2020-07-25 00:41:27 +08:00
if ( ! angle | | angle % 90 ) {
2016-04-04 22:11:48 +08:00
return ;
}
unsigned int new_height = height ;
unsigned int new_width = width ;
uint8_t * rotate_buffer = AllocBuffer ( size ) ;
2020-07-25 00:41:27 +08:00
switch ( angle ) {
2016-04-04 22:11:48 +08:00
case 90 :
2017-04-28 22:34:48 +08:00
{
new_height = width ;
new_width = height ;
2016-04-04 22:11:48 +08:00
2017-04-28 22:34:48 +08:00
unsigned int line_bytes = new_width * colours ;
unsigned char * s_ptr = buffer ;
2016-04-04 22:11:48 +08:00
2017-11-17 20:52:26 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int i = new_width ; i > 0 ; i - - ) {
unsigned char * d_ptr = rotate_buffer + ( i - 1 ) ;
for ( unsigned int j = new_height ; j > 0 ; j - - ) {
2017-04-28 22:34:48 +08:00
* d_ptr = * s_ptr + + ;
d_ptr + = line_bytes ;
}
2016-04-04 22:11:48 +08:00
}
2017-11-17 20:52:26 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
2017-04-28 22:34:48 +08:00
Rgb * s_rptr = ( Rgb * ) s_ptr ;
2020-07-25 00:41:27 +08:00
for ( unsigned int i = new_width ; i ; i - - ) {
2017-11-17 20:52:26 +08:00
Rgb * d_rptr = ( Rgb * ) ( rotate_buffer + ( ( i - 1 ) < < 2 ) ) ;
2020-07-25 00:41:27 +08:00
for ( unsigned int j = new_height ; j ; j - - ) {
2017-04-28 22:34:48 +08:00
* d_rptr = * s_rptr + + ;
d_rptr + = new_width ;
}
2016-04-04 22:11:48 +08:00
}
2017-11-17 20:52:26 +08:00
} else /* Assume RGB24 */ {
2020-07-25 00:41:27 +08:00
for ( unsigned int i = new_width ; i ; i - - ) {
2017-11-17 20:52:26 +08:00
unsigned char * d_ptr = rotate_buffer + ( ( i - 1 ) * 3 ) ;
2020-07-25 00:41:27 +08:00
for ( unsigned int j = new_height ; j ; j - - ) {
2017-04-28 22:34:48 +08:00
* d_ptr = * s_ptr + + ;
* ( d_ptr + 1 ) = * s_ptr + + ;
* ( d_ptr + 2 ) = * s_ptr + + ;
d_ptr + = line_bytes ;
}
2016-04-04 22:11:48 +08:00
}
}
2017-04-28 22:34:48 +08:00
break ;
2016-04-04 22:11:48 +08:00
}
case 180 :
{
2017-04-28 22:34:48 +08:00
unsigned char * s_ptr = buffer + size ;
unsigned char * d_ptr = rotate_buffer ;
2017-11-17 20:52:26 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
while ( s_ptr > buffer ) {
2017-04-28 22:34:48 +08:00
s_ptr - - ;
* d_ptr + + = * s_ptr ;
}
2017-11-17 20:52:26 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
2017-04-28 22:34:48 +08:00
Rgb * s_rptr = ( Rgb * ) s_ptr ;
Rgb * d_rptr = ( Rgb * ) d_ptr ;
2017-11-17 20:52:26 +08:00
while ( s_rptr > ( Rgb * ) buffer ) {
2017-04-28 22:34:48 +08:00
s_rptr - - ;
* d_rptr + + = * s_rptr ;
}
2017-11-17 20:52:26 +08:00
} else /* Assume RGB24 */ {
while ( s_ptr > buffer ) {
2017-04-28 22:34:48 +08:00
s_ptr - = 3 ;
* d_ptr + + = * s_ptr ;
* d_ptr + + = * ( s_ptr + 1 ) ;
* d_ptr + + = * ( s_ptr + 2 ) ;
}
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
break ;
2016-04-04 22:11:48 +08:00
}
case 270 :
2017-04-28 22:34:48 +08:00
{
new_height = width ;
new_width = height ;
2016-04-04 22:11:48 +08:00
2017-04-28 22:34:48 +08:00
unsigned int line_bytes = new_width * colours ;
unsigned char * s_ptr = buffer + size ;
2016-04-04 22:11:48 +08:00
2017-11-17 20:52:26 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int i = new_width ; i > 0 ; i - - ) {
unsigned char * d_ptr = rotate_buffer + ( i - 1 ) ;
for ( unsigned int j = new_height ; j > 0 ; j - - ) {
2017-04-28 22:34:48 +08:00
s_ptr - - ;
* d_ptr = * s_ptr ;
d_ptr + = line_bytes ;
}
2016-04-04 22:11:48 +08:00
}
2017-11-17 20:52:26 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
2017-04-28 22:34:48 +08:00
Rgb * s_rptr = ( Rgb * ) s_ptr ;
2017-11-17 20:52:26 +08:00
for ( unsigned int i = new_width ; i > 0 ; i - - ) {
Rgb * d_rptr = ( Rgb * ) ( rotate_buffer + ( ( i - 1 ) < < 2 ) ) ;
for ( unsigned int j = new_height ; j > 0 ; j - - ) {
2017-04-28 22:34:48 +08:00
s_rptr - - ;
* d_rptr = * s_rptr ;
d_rptr + = new_width ;
}
2016-04-04 22:11:48 +08:00
}
2017-11-17 20:52:26 +08:00
} else /* Assume RGB24 */ {
for ( unsigned int i = new_width ; i > 0 ; i - - ) {
unsigned char * d_ptr = rotate_buffer + ( ( i - 1 ) * 3 ) ;
for ( unsigned int j = new_height ; j > 0 ; j - - ) {
2017-04-28 22:34:48 +08:00
* ( d_ptr + 2 ) = * ( - - s_ptr ) ;
* ( d_ptr + 1 ) = * ( - - s_ptr ) ;
* d_ptr = * ( - - s_ptr ) ;
d_ptr + = line_bytes ;
}
2016-04-04 22:11:48 +08:00
}
}
2017-04-28 22:34:48 +08:00
break ;
2016-04-04 22:11:48 +08:00
}
}
2017-04-28 22:34:48 +08:00
2020-07-25 00:41:27 +08:00
AssignDirect ( new_width , new_height , colours , subpixelorder , rotate_buffer , size , ZM_BUFTYPE_ZM ) ;
} // void Image::Rotate(int angle)
2013-03-17 07:45:21 +08:00
/* RGB32 compatible: complete */
2017-11-17 20:52:26 +08:00
void Image : : Flip ( bool leftright ) {
2016-04-04 22:11:48 +08:00
uint8_t * flip_buffer = AllocBuffer ( size ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
unsigned int line_bytes = width * colours ;
unsigned int line_bytes2 = 2 * line_bytes ;
2017-11-17 20:52:26 +08:00
if ( leftright ) {
2016-04-04 22:11:48 +08:00
// Horizontal flip, left to right
unsigned char * s_ptr = buffer + line_bytes ;
unsigned char * d_ptr = flip_buffer ;
unsigned char * max_d_ptr = flip_buffer + size ;
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
while ( d_ptr < max_d_ptr ) {
for ( unsigned int j = 0 ; j < width ; j + + ) {
2016-04-04 22:11:48 +08:00
s_ptr - - ;
* d_ptr + + = * s_ptr ;
}
s_ptr + = line_bytes2 ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
2016-04-04 22:11:48 +08:00
Rgb * s_rptr = ( Rgb * ) s_ptr ;
Rgb * d_rptr = ( Rgb * ) flip_buffer ;
Rgb * max_d_rptr = ( Rgb * ) max_d_ptr ;
2018-10-22 22:41:27 +08:00
while ( d_rptr < max_d_rptr ) {
for ( unsigned int j = 0 ; j < width ; j + + ) {
2016-04-04 22:11:48 +08:00
s_rptr - - ;
* d_rptr + + = * s_rptr ;
}
s_rptr + = width * 2 ;
}
2018-10-22 22:41:27 +08:00
} else /* Assume RGB24 */ {
while ( d_ptr < max_d_ptr ) {
for ( unsigned int j = 0 ; j < width ; j + + ) {
2016-04-04 22:11:48 +08:00
s_ptr - = 3 ;
* d_ptr + + = * s_ptr ;
* d_ptr + + = * ( s_ptr + 1 ) ;
* d_ptr + + = * ( s_ptr + 2 ) ;
}
s_ptr + = line_bytes2 ;
}
2019-09-17 22:31:48 +08:00
}
2018-10-22 22:41:27 +08:00
} else {
2016-04-04 22:11:48 +08:00
// Vertical flip, top to bottom
unsigned char * s_ptr = buffer + ( height * line_bytes ) ;
unsigned char * d_ptr = flip_buffer ;
2020-07-25 03:26:28 +08:00
while ( s_ptr > buffer ) {
2016-04-04 22:11:48 +08:00
s_ptr - = line_bytes ;
2020-07-25 03:26:28 +08:00
memcpy ( d_ptr , s_ptr , line_bytes ) ;
2016-04-04 22:11:48 +08:00
d_ptr + = line_bytes ;
}
}
2017-04-28 22:34:48 +08:00
2020-07-25 00:41:27 +08:00
AssignDirect ( width , height , colours , subpixelorder , flip_buffer , size , ZM_BUFTYPE_ZM ) ;
2013-03-17 07:45:21 +08:00
}
2020-07-24 05:14:56 +08:00
void Image : : Scale ( unsigned int factor ) {
2018-10-22 22:41:27 +08:00
if ( ! factor ) {
2020-07-17 02:50:50 +08:00
Error ( " Bogus scale factor %d found " , factor ) ;
2016-04-04 22:11:48 +08:00
return ;
}
2018-10-22 22:41:27 +08:00
if ( factor = = ZM_SCALE_BASE ) {
2016-04-04 22:11:48 +08:00
return ;
}
unsigned int new_width = ( width * factor ) / ZM_SCALE_BASE ;
unsigned int new_height = ( height * factor ) / ZM_SCALE_BASE ;
2017-04-28 22:34:48 +08:00
2020-07-17 02:50:50 +08:00
// Why larger than we need?
2016-04-04 22:11:48 +08:00
size_t scale_buffer_size = ( new_width + 1 ) * ( new_height + 1 ) * colours ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
uint8_t * scale_buffer = AllocBuffer ( scale_buffer_size ) ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( factor > ZM_SCALE_BASE ) {
2016-04-04 22:11:48 +08:00
unsigned char * pd = scale_buffer ;
unsigned int wc = width * colours ;
unsigned int nwc = new_width * colours ;
unsigned int h_count = ZM_SCALE_BASE / 2 ;
unsigned int last_h_index = 0 ;
unsigned int last_w_index = 0 ;
unsigned int h_index ;
2018-10-22 22:41:27 +08:00
for ( unsigned int y = 0 ; y < height ; y + + ) {
2016-04-04 22:11:48 +08:00
unsigned char * ps = & buffer [ y * wc ] ;
unsigned int w_count = ZM_SCALE_BASE / 2 ;
unsigned int w_index ;
last_w_index = 0 ;
2018-10-22 22:41:27 +08:00
for ( unsigned int x = 0 ; x < width ; x + + ) {
2016-04-04 22:11:48 +08:00
w_count + = factor ;
w_index = w_count / ZM_SCALE_BASE ;
2018-10-22 22:41:27 +08:00
for ( unsigned int f = last_w_index ; f < w_index ; f + + ) {
for ( unsigned int c = 0 ; c < colours ; c + + ) {
2016-04-04 22:11:48 +08:00
* pd + + = * ( ps + c ) ;
}
}
ps + = colours ;
last_w_index = w_index ;
}
h_count + = factor ;
h_index = h_count / ZM_SCALE_BASE ;
2018-10-22 22:41:27 +08:00
for ( unsigned int f = last_h_index + 1 ; f < h_index ; f + + ) {
2021-02-03 06:26:44 +08:00
memcpy ( pd , pd - nwc , nwc ) ;
2016-04-04 22:11:48 +08:00
pd + = nwc ;
}
last_h_index = h_index ;
2021-02-03 06:26:44 +08:00
} // end foreach line
2016-04-04 22:11:48 +08:00
new_width = last_w_index ;
new_height = last_h_index ;
2018-10-22 22:41:27 +08:00
} else {
2016-04-04 22:11:48 +08:00
unsigned char * pd = scale_buffer ;
unsigned int wc = width * colours ;
unsigned int xstart = factor / 2 ;
unsigned int ystart = factor / 2 ;
unsigned int h_count = ystart ;
unsigned int last_h_index = 0 ;
unsigned int last_w_index = 0 ;
unsigned int h_index ;
2020-07-24 05:14:56 +08:00
for ( unsigned int y = 0 ; y < height ; y + + ) {
2016-04-04 22:11:48 +08:00
h_count + = factor ;
h_index = h_count / ZM_SCALE_BASE ;
2018-10-22 22:41:27 +08:00
if ( h_index > last_h_index ) {
2016-04-04 22:11:48 +08:00
unsigned int w_count = xstart ;
unsigned int w_index ;
last_w_index = 0 ;
unsigned char * ps = & buffer [ y * wc ] ;
2020-07-24 05:14:56 +08:00
for ( unsigned int x = 0 ; x < width ; x + + ) {
2016-04-04 22:11:48 +08:00
w_count + = factor ;
w_index = w_count / ZM_SCALE_BASE ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( w_index > last_w_index ) {
for ( unsigned int c = 0 ; c < colours ; c + + ) {
2016-04-04 22:11:48 +08:00
* pd + + = * ps + + ;
}
2018-10-22 22:41:27 +08:00
} else {
2016-04-04 22:11:48 +08:00
ps + = colours ;
}
last_w_index = w_index ;
}
}
last_h_index = h_index ;
}
new_width = last_w_index ;
new_height = last_h_index ;
2020-07-17 02:50:50 +08:00
} // end foreach line
2020-07-24 05:14:56 +08:00
AssignDirect ( new_width , new_height , colours , subpixelorder , scale_buffer , scale_buffer_size , ZM_BUFTYPE_ZM ) ;
2013-03-17 07:45:21 +08:00
}
2018-10-22 22:41:27 +08:00
void Image : : Deinterlace_Discard ( ) {
2016-04-04 22:11:48 +08:00
/* Simple deinterlacing. Copy the even lines into the odd lines */
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
2016-04-04 22:11:48 +08:00
const uint8_t * psrc ;
uint8_t * pdest ;
2018-10-22 22:41:27 +08:00
for ( unsigned int y = 0 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
psrc = buffer + ( y * width ) ;
pdest = buffer + ( ( y + 1 ) * width ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pdest + + = * psrc + + ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
2016-04-04 22:11:48 +08:00
const uint8_t * psrc ;
uint8_t * pdest ;
2018-10-22 22:41:27 +08:00
for ( unsigned int y = 0 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
psrc = buffer + ( ( y * width ) * 3 ) ;
pdest = buffer + ( ( ( y + 1 ) * width ) * 3 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pdest + + = * psrc + + ;
* pdest + + = * psrc + + ;
* pdest + + = * psrc + + ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
2016-04-04 22:11:48 +08:00
const Rgb * psrc ;
Rgb * pdest ;
2018-10-22 22:41:27 +08:00
for ( unsigned int y = 0 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
psrc = ( Rgb * ) ( buffer + ( ( y * width ) < < 2 ) ) ;
pdest = ( Rgb * ) ( buffer + ( ( ( y + 1 ) * width ) < < 2 ) ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pdest + + = * psrc + + ;
}
}
} else {
Error ( " Deinterlace called with unexpected colours: %d " , colours ) ;
}
2013-03-17 07:45:21 +08:00
}
2018-10-22 22:41:27 +08:00
void Image : : Deinterlace_Linear ( ) {
2016-04-04 22:11:48 +08:00
/* Simple deinterlacing. The odd lines are average of the line above and line below */
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
const uint8_t * pbelow , * pabove ;
uint8_t * pcurrent ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) ( height - 1 ) ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( y - 1 ) * width ) ;
pbelow = buffer + ( ( y + 1 ) * width ) ;
pcurrent = buffer + ( y * width ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pcurrent + + = ( * pabove + + + * pbelow + + ) > > 1 ;
}
}
/* Special case for the last line */
pcurrent = buffer + ( ( height - 1 ) * width ) ;
pabove = buffer + ( ( height - 2 ) * width ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pcurrent + + = * pabove + + ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) ( height - 1 ) ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( ( y - 1 ) * width ) * 3 ) ;
pbelow = buffer + ( ( ( y + 1 ) * width ) * 3 ) ;
pcurrent = buffer + ( ( y * width ) * 3 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pcurrent + + = ( * pabove + + + * pbelow + + ) > > 1 ;
* pcurrent + + = ( * pabove + + + * pbelow + + ) > > 1 ;
* pcurrent + + = ( * pabove + + + * pbelow + + ) > > 1 ;
}
}
/* Special case for the last line */
pcurrent = buffer + ( ( ( height - 1 ) * width ) * 3 ) ;
pabove = buffer + ( ( ( height - 2 ) * width ) * 3 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pcurrent + + = * pabove + + ;
* pcurrent + + = * pabove + + ;
* pcurrent + + = * pabove + + ;
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) ( height - 1 ) ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( ( y - 1 ) * width ) < < 2 ) ;
pbelow = buffer + ( ( ( y + 1 ) * width ) < < 2 ) ;
pcurrent = buffer + ( ( y * width ) < < 2 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pcurrent + + = ( * pabove + + + * pbelow + + ) > > 1 ;
* pcurrent + + = ( * pabove + + + * pbelow + + ) > > 1 ;
* pcurrent + + = ( * pabove + + + * pbelow + + ) > > 1 ;
* pcurrent + + = ( * pabove + + + * pbelow + + ) > > 1 ;
}
}
/* Special case for the last line */
pcurrent = buffer + ( ( ( height - 1 ) * width ) < < 2 ) ;
pabove = buffer + ( ( ( height - 2 ) * width ) < < 2 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pcurrent + + = * pabove + + ;
* pcurrent + + = * pabove + + ;
2019-09-17 22:31:48 +08:00
* pcurrent + + = * pabove + + ;
2016-04-04 22:11:48 +08:00
* pcurrent + + = * pabove + + ;
}
} else {
Error ( " Deinterlace called with unexpected colours: %d " , colours ) ;
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
}
2018-10-22 22:41:27 +08:00
void Image : : Deinterlace_Blend ( ) {
2016-04-04 22:11:48 +08:00
/* Simple deinterlacing. Blend the fields together. 50% blend */
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
uint8_t * pabove , * pcurrent ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( y - 1 ) * width ) ;
pcurrent = buffer + ( y * width ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pabove = ( * pabove + * pcurrent ) > > 1 ;
* pcurrent + + = * pabove + + ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( ( y - 1 ) * width ) * 3 ) ;
pcurrent = buffer + ( ( y * width ) * 3 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pabove = ( * pabove + * pcurrent ) > > 1 ;
* pcurrent + + = * pabove + + ;
* pabove = ( * pabove + * pcurrent ) > > 1 ;
* pcurrent + + = * pabove + + ;
* pabove = ( * pabove + * pcurrent ) > > 1 ;
* pcurrent + + = * pabove + + ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( ( y - 1 ) * width ) < < 2 ) ;
pcurrent = buffer + ( ( y * width ) < < 2 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
* pabove = ( * pabove + * pcurrent ) > > 1 ;
* pcurrent + + = * pabove + + ;
* pabove = ( * pabove + * pcurrent ) > > 1 ;
* pcurrent + + = * pabove + + ;
* pabove = ( * pabove + * pcurrent ) > > 1 ;
* pcurrent + + = * pabove + + ;
* pabove = ( * pabove + * pcurrent ) > > 1 ;
* pcurrent + + = * pabove + + ;
}
}
} else {
Error ( " Deinterlace called with unexpected colours: %d " , colours ) ;
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
}
2018-10-22 22:41:27 +08:00
void Image : : Deinterlace_Blend_CustomRatio ( int divider ) {
2016-04-04 22:11:48 +08:00
/* Simple deinterlacing. Blend the fields together at a custom ratio. */
/* 1 = 50% blending */
/* 2 = 25% blending */
/* 3 = 12.% blending */
/* 4 = 6.25% blending */
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
uint8_t * pabove , * pcurrent ;
uint8_t subpix1 , subpix2 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
if ( divider < 1 | | divider > 4 ) {
Error ( " Deinterlace called with invalid blend ratio " ) ;
}
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( colours = = ZM_COLOUR_GRAY8 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( y - 1 ) * width ) ;
pcurrent = buffer + ( y * width ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
subpix1 = ( ( * pabove - * pcurrent ) > > divider ) + * pcurrent ;
subpix2 = ( ( * pcurrent - * pabove ) > > divider ) + * pabove ;
* pcurrent + + = subpix1 ;
* pabove + + = subpix2 ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB24 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( ( y - 1 ) * width ) * 3 ) ;
pcurrent = buffer + ( ( y * width ) * 3 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
subpix1 = ( ( * pabove - * pcurrent ) > > divider ) + * pcurrent ;
subpix2 = ( ( * pcurrent - * pabove ) > > divider ) + * pabove ;
* pcurrent + + = subpix1 ;
* pabove + + = subpix2 ;
subpix1 = ( ( * pabove - * pcurrent ) > > divider ) + * pcurrent ;
subpix2 = ( ( * pcurrent - * pabove ) > > divider ) + * pabove ;
* pcurrent + + = subpix1 ;
* pabove + + = subpix2 ;
subpix1 = ( ( * pabove - * pcurrent ) > > divider ) + * pcurrent ;
subpix2 = ( ( * pcurrent - * pabove ) > > divider ) + * pabove ;
* pcurrent + + = subpix1 ;
* pabove + + = subpix2 ;
}
}
2018-10-22 22:41:27 +08:00
} else if ( colours = = ZM_COLOUR_RGB32 ) {
for ( unsigned int y = 1 ; y < ( unsigned int ) height ; y + = 2 ) {
2016-04-04 22:11:48 +08:00
pabove = buffer + ( ( ( y - 1 ) * width ) < < 2 ) ;
pcurrent = buffer + ( ( y * width ) < < 2 ) ;
for ( unsigned int x = 0 ; x < ( unsigned int ) width ; x + + ) {
subpix1 = ( ( * pabove - * pcurrent ) > > divider ) + * pcurrent ;
subpix2 = ( ( * pcurrent - * pabove ) > > divider ) + * pabove ;
* pcurrent + + = subpix1 ;
* pabove + + = subpix2 ;
subpix1 = ( ( * pabove - * pcurrent ) > > divider ) + * pcurrent ;
subpix2 = ( ( * pcurrent - * pabove ) > > divider ) + * pabove ;
* pcurrent + + = subpix1 ;
* pabove + + = subpix2 ;
subpix1 = ( ( * pabove - * pcurrent ) > > divider ) + * pcurrent ;
subpix2 = ( ( * pcurrent - * pabove ) > > divider ) + * pabove ;
* pcurrent + + = subpix1 ;
* pabove + + = subpix2 ;
subpix1 = ( ( * pabove - * pcurrent ) > > divider ) + * pcurrent ;
subpix2 = ( ( * pcurrent - * pabove ) > > divider ) + * pabove ;
* pcurrent + + = subpix1 ;
* pabove + + = subpix2 ;
}
}
} else {
Error ( " Deinterlace called with unexpected colours: %d " , colours ) ;
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
}
void Image : : Deinterlace_4Field ( const Image * next_image , unsigned int threshold )
{
2016-04-04 22:11:48 +08:00
if ( ! ( width = = next_image - > width & & height = = next_image - > height & & colours = = next_image - > colours & & subpixelorder = = next_image - > subpixelorder ) )
{
Panic ( " Attempt to deinterlace different sized images, expected %dx%dx%d %d, got %dx%dx%d %d " , width , height , colours , subpixelorder , next_image - > width , next_image - > height , next_image - > colours , next_image - > subpixelorder ) ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
switch ( colours ) {
case ZM_COLOUR_RGB24 :
2017-04-28 22:34:48 +08:00
{
if ( subpixelorder = = ZM_SUBPIX_ORDER_BGR ) {
/* BGR subpixel order */
std_deinterlace_4field_bgr ( buffer , next_image - > buffer , threshold , width , height ) ;
} else {
/* Assume RGB subpixel order */
std_deinterlace_4field_rgb ( buffer , next_image - > buffer , threshold , width , height ) ;
}
break ;
}
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_RGB32 :
2017-04-28 22:34:48 +08:00
{
if ( subpixelorder = = ZM_SUBPIX_ORDER_ARGB ) {
/* ARGB subpixel order */
( * fptr_deinterlace_4field_argb ) ( buffer , next_image - > buffer , threshold , width , height ) ;
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_ABGR ) {
/* ABGR subpixel order */
( * fptr_deinterlace_4field_abgr ) ( buffer , next_image - > buffer , threshold , width , height ) ;
} else if ( subpixelorder = = ZM_SUBPIX_ORDER_BGRA ) {
/* BGRA subpixel order */
( * fptr_deinterlace_4field_bgra ) ( buffer , next_image - > buffer , threshold , width , height ) ;
} else {
/* Assume RGBA subpixel order */
( * fptr_deinterlace_4field_rgba ) ( buffer , next_image - > buffer , threshold , width , height ) ;
}
break ;
}
2016-04-04 22:11:48 +08:00
case ZM_COLOUR_GRAY8 :
2017-04-28 22:34:48 +08:00
( * fptr_deinterlace_4field_gray8 ) ( buffer , next_image - > buffer , threshold , width , height ) ;
break ;
2016-04-04 22:11:48 +08:00
default :
2017-04-28 22:34:48 +08:00
Panic ( " Deinterlace_4Field called with unexpected colours: %d " , colours ) ;
break ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
}
/************************************************* BLEND FUNCTIONS *************************************************/
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " sse2 " ) ) )
# endif
void sse2_fastblend ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count , double blendpercent ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2016-04-04 22:11:48 +08:00
static uint32_t divider = 0 ;
static uint32_t clearmask = 0 ;
static double current_blendpercent = 0.0 ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( current_blendpercent ! = blendpercent ) {
2016-04-04 22:11:48 +08:00
/* Attempt to match the blending percent to one of the possible values */
2018-10-22 22:41:27 +08:00
if ( blendpercent < 2.34375 ) {
2016-04-04 22:11:48 +08:00
// 1.5625% blending
divider = 6 ;
clearmask = 0x03030303 ;
2018-10-22 22:41:27 +08:00
} else if ( blendpercent < 4.6875 ) {
2016-04-04 22:11:48 +08:00
// 3.125% blending
divider = 5 ;
clearmask = 0x07070707 ;
2018-10-22 22:41:27 +08:00
} else if ( blendpercent < 9.375 ) {
2016-04-04 22:11:48 +08:00
// 6.25% blending
divider = 4 ;
clearmask = 0x0F0F0F0F ;
2018-10-22 22:41:27 +08:00
} else if ( blendpercent < 18.75 ) {
2016-04-04 22:11:48 +08:00
// 12.5% blending
divider = 3 ;
clearmask = 0x1F1F1F1F ;
2018-10-22 22:41:27 +08:00
} else if ( blendpercent < 37.5 ) {
2016-04-04 22:11:48 +08:00
// 25% blending
divider = 2 ;
clearmask = 0x3F3F3F3F ;
} else {
// 50% blending
divider = 1 ;
clearmask = 0x7F7F7F7F ;
}
current_blendpercent = blendpercent ;
}
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" movd %4, %%xmm3 \n \t "
" movd %5, %%xmm4 \n \t "
" pshufd $0x0, %%xmm3, %%xmm3 \n \t "
" sub $0x10, %0 \n \t "
" sub $0x10, %1 \n \t "
" sub $0x10, %2 \n \t "
" sse2_fastblend_iter: \n \t "
" movdqa (%0,%3),%%xmm0 \n \t "
" movdqa %%xmm0,%%xmm2 \n \t "
" movdqa (%1,%3),%%xmm1 \n \t "
" psrlq %%xmm4,%%xmm0 \n \t "
" psrlq %%xmm4,%%xmm1 \n \t "
" pand %%xmm3,%%xmm1 \n \t "
" pand %%xmm3,%%xmm0 \n \t "
" psubb %%xmm0,%%xmm1 \n \t "
" paddb %%xmm2,%%xmm1 \n \t "
" movntdq %%xmm1,(%2,%3) \n \t "
" sub $0x10, %3 \n \t "
" jnz sse2_fastblend_iter \n \t "
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count ) , " m " ( clearmask ) , " m " ( divider )
: " %xmm0 " , " %xmm1 " , " %xmm2 " , " %xmm3 " , " %xmm4 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
__attribute__ ( ( noinline ) ) void std_fastblend ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count , double blendpercent ) {
2016-04-04 22:11:48 +08:00
static int divider = 0 ;
static double current_blendpercent = 0.0 ;
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
if ( current_blendpercent ! = blendpercent ) {
2016-04-04 22:11:48 +08:00
/* Attempt to match the blending percent to one of the possible values */
2018-10-22 22:41:27 +08:00
if ( blendpercent < 2.34375 ) {
2016-04-04 22:11:48 +08:00
// 1.5625% blending
divider = 6 ;
2018-10-22 22:41:27 +08:00
} else if ( blendpercent < 4.6875 ) {
2016-04-04 22:11:48 +08:00
// 3.125% blending
divider = 5 ;
2018-10-22 22:41:27 +08:00
} else if ( blendpercent < 9.375 ) {
2016-04-04 22:11:48 +08:00
// 6.25% blending
divider = 4 ;
2018-10-22 22:41:27 +08:00
} else if ( blendpercent < 18.75 ) {
2016-04-04 22:11:48 +08:00
// 12.5% blending
divider = 3 ;
2018-10-22 22:41:27 +08:00
} else if ( blendpercent < 37.5 ) {
2016-04-04 22:11:48 +08:00
// 25% blending
divider = 2 ;
} else {
// 50% blending
divider = 1 ;
}
current_blendpercent = blendpercent ;
}
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
while ( result < max_ptr ) {
2016-04-04 22:11:48 +08:00
result [ 0 ] = ( ( col2 [ 0 ] - col1 [ 0 ] ) > > divider ) + col1 [ 0 ] ;
result [ 1 ] = ( ( col2 [ 1 ] - col1 [ 1 ] ) > > divider ) + col1 [ 1 ] ;
result [ 2 ] = ( ( col2 [ 2 ] - col1 [ 2 ] ) > > divider ) + col1 [ 2 ] ;
result [ 3 ] = ( ( col2 [ 3 ] - col1 [ 3 ] ) > > divider ) + col1 [ 3 ] ;
result [ 4 ] = ( ( col2 [ 4 ] - col1 [ 4 ] ) > > divider ) + col1 [ 4 ] ;
result [ 5 ] = ( ( col2 [ 5 ] - col1 [ 5 ] ) > > divider ) + col1 [ 5 ] ;
result [ 6 ] = ( ( col2 [ 6 ] - col1 [ 6 ] ) > > divider ) + col1 [ 6 ] ;
result [ 7 ] = ( ( col2 [ 7 ] - col1 [ 7 ] ) > > divider ) + col1 [ 7 ] ;
result [ 8 ] = ( ( col2 [ 8 ] - col1 [ 8 ] ) > > divider ) + col1 [ 8 ] ;
result [ 9 ] = ( ( col2 [ 9 ] - col1 [ 9 ] ) > > divider ) + col1 [ 9 ] ;
result [ 10 ] = ( ( col2 [ 10 ] - col1 [ 10 ] ) > > divider ) + col1 [ 10 ] ;
result [ 11 ] = ( ( col2 [ 11 ] - col1 [ 11 ] ) > > divider ) + col1 [ 11 ] ;
result [ 12 ] = ( ( col2 [ 12 ] - col1 [ 12 ] ) > > divider ) + col1 [ 12 ] ;
result [ 13 ] = ( ( col2 [ 13 ] - col1 [ 13 ] ) > > divider ) + col1 [ 13 ] ;
result [ 14 ] = ( ( col2 [ 14 ] - col1 [ 14 ] ) > > divider ) + col1 [ 14 ] ;
result [ 15 ] = ( ( col2 [ 15 ] - col1 [ 15 ] ) > > divider ) + col1 [ 15 ] ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
col2 + = 16 ;
result + = 16 ;
}
2013-03-17 07:45:21 +08:00
}
2017-03-19 19:31:44 +08:00
/* FastBlend Neon for AArch32 */
2018-10-12 22:34:16 +08:00
# if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON))
2017-03-19 19:31:44 +08:00
__attribute__ ( ( noinline , __target__ ( " fpu=neon " ) ) )
# endif
void neon32_armv7_fastblend ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count , double blendpercent ) {
2018-10-12 22:34:16 +08:00
# if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON))
2017-03-19 19:31:44 +08:00
static int8_t divider = 0 ;
static double current_blendpercent = 0.0 ;
if ( current_blendpercent ! = blendpercent ) {
/* Attempt to match the blending percent to one of the possible values */
if ( blendpercent < 2.34375 ) {
// 1.5625% blending
divider = 6 ;
} else if ( blendpercent > = 2.34375 & & blendpercent < 4.6875 ) {
// 3.125% blending
divider = 5 ;
} else if ( blendpercent > = 4.6875 & & blendpercent < 9.375 ) {
// 6.25% blending
divider = 4 ;
} else if ( blendpercent > = 9.375 & & blendpercent < 18.75 ) {
// 12.5% blending
divider = 3 ;
} else if ( blendpercent > = 18.75 & & blendpercent < 37.5 ) {
// 25% blending
divider = 2 ;
} else if ( blendpercent > = 37.5 ) {
// 50% blending
divider = 1 ;
}
// We only have instruction to shift left by a variable, going negative shifts right :)
divider * = - 1 ;
current_blendpercent = blendpercent ;
}
2017-04-16 15:57:37 +08:00
/* Q0(D0,D1) = col1+0 */
/* Q1(D2,D3) = col1+16 */
/* Q2(D4,D5) = col1+32 */
/* Q3(D6,D7) = col1+48 */
/* Q4(D8,D9) = col2+0 */
/* Q5(D10,D11) = col2+16 */
/* Q6(D12,D13) = col2+32 */
/* Q7(D14,D15) = col2+48 */
/* Q8(D16,D17) = col1tmp+0 */
/* Q9(D18,D19) = col1tmp+16 */
/* Q10(D20,D21) = col1tmp+32 */
/* Q11(D22,D23) = col1tmp+48 */
/* Q12(D24,D25) = divider */
2017-03-19 19:31:44 +08:00
__asm__ __volatile__ (
" mov r12, %4 \n \t "
2017-04-16 15:57:37 +08:00
" vdup.8 q12, r12 \n \t "
2019-09-03 23:55:37 +08:00
" neon32_armv7_fastblend_iter%=: \n \t "
2017-04-16 15:57:37 +08:00
" vldm %0!, {q0,q1,q2,q3} \n \t "
" vldm %1!, {q4,q5,q6,q7} \n \t "
2017-05-14 00:08:18 +08:00
" pld [%0, #256] \n \t "
" pld [%1, #256] \n \t "
2017-04-16 15:57:37 +08:00
" vrshl.u8 q8, q0, q12 \n \t "
" vrshl.u8 q9, q1, q12 \n \t "
" vrshl.u8 q10, q2, q12 \n \t "
" vrshl.u8 q11, q3, q12 \n \t "
" vrshl.u8 q4, q4, q12 \n \t "
" vrshl.u8 q5, q5, q12 \n \t "
" vrshl.u8 q6, q6, q12 \n \t "
" vrshl.u8 q7, q7, q12 \n \t "
" vsub.i8 q4, q4, q8 \n \t "
" vsub.i8 q5, q5, q9 \n \t "
" vsub.i8 q6, q6, q10 \n \t "
" vsub.i8 q7, q7, q11 \n \t "
" vadd.i8 q4, q4, q0 \n \t "
" vadd.i8 q5, q5, q1 \n \t "
" vadd.i8 q6, q6, q2 \n \t "
" vadd.i8 q7, q7, q3 \n \t "
" vstm %2!, {q4,q5,q6,q7} \n \t "
" subs %3, %3, #64 \n \t "
2019-09-03 23:55:37 +08:00
" bne neon32_armv7_fastblend_iter%= \n \t "
2017-03-19 19:31:44 +08:00
:
2017-04-16 15:57:37 +08:00
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count ) , " r " ( divider )
: " %r12 " , " %q0 " , " %q1 " , " %q2 " , " %q3 " , " %q4 " , " %q5 " , " %q6 " , " %q7 " , " %q8 " , " %q9 " , " %q10 " , " %q11 " , " %q12 " , " cc " , " memory "
2017-03-19 19:31:44 +08:00
) ;
2017-03-19 19:39:24 +08:00
# else
2017-03-26 02:49:15 +08:00
Panic ( " Neon function called on a non-ARM platform or Neon code is absent " ) ;
2017-03-19 19:39:24 +08:00
# endif
2017-03-19 19:31:44 +08:00
}
2017-05-13 03:59:08 +08:00
__attribute__ ( ( noinline ) ) void neon64_armv8_fastblend ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count , double blendpercent ) {
# if (defined(__aarch64__) && !defined(ZM_STRIP_NEON))
static int8_t divider = 0 ;
static double current_blendpercent = 0.0 ;
if ( current_blendpercent ! = blendpercent ) {
/* Attempt to match the blending percent to one of the possible values */
if ( blendpercent < 2.34375 ) {
// 1.5625% blending
divider = 6 ;
} else if ( blendpercent > = 2.34375 & & blendpercent < 4.6875 ) {
// 3.125% blending
divider = 5 ;
} else if ( blendpercent > = 4.6875 & & blendpercent < 9.375 ) {
// 6.25% blending
divider = 4 ;
} else if ( blendpercent > = 9.375 & & blendpercent < 18.75 ) {
// 12.5% blending
divider = 3 ;
} else if ( blendpercent > = 18.75 & & blendpercent < 37.5 ) {
// 25% blending
divider = 2 ;
} else if ( blendpercent > = 37.5 ) {
// 50% blending
divider = 1 ;
}
// We only have instruction to shift left by a variable, going negative shifts right :)
divider * = - 1 ;
current_blendpercent = blendpercent ;
}
/* V16 = col1+0 */
/* V17 = col1+16 */
/* V18 = col1+32 */
/* V19 = col1+48 */
/* V20 = col2+0 */
/* V21 = col2+16 */
/* V22 = col2+32 */
/* V23 = col2+48 */
/* V24 = col1tmp+0 */
/* V25 = col1tmp+16 */
/* V26 = col1tmp+32 */
/* V27 = col1tmp+48 */
/* V28 = divider */
__asm__ __volatile__ (
" mov x12, %4 \n \t "
" dup v28.16b, w12 \n \t "
2019-09-03 23:55:37 +08:00
" neon64_armv8_fastblend_iter%=: \n \t "
2017-05-13 03:59:08 +08:00
" ldp q16, q17, [%0], #32 \n \t "
" ldp q18, q19, [%0], #32 \n \t "
" ldp q20, q21, [%1], #32 \n \t "
" ldp q22, q23, [%1], #32 \n \t "
2017-05-14 00:08:18 +08:00
" prfm pldl1keep, [%0, #256] \n \t "
" prfm pldl1keep, [%1, #256] \n \t "
2017-05-13 03:59:08 +08:00
" urshl v24.16b, v16.16b, v28.16b \n \t "
" urshl v25.16b, v17.16b, v28.16b \n \t "
" urshl v26.16b, v18.16b, v28.16b \n \t "
" urshl v27.16b, v19.16b, v28.16b \n \t "
" urshl v20.16b, v20.16b, v28.16b \n \t "
" urshl v21.16b, v21.16b, v28.16b \n \t "
" urshl v22.16b, v22.16b, v28.16b \n \t "
" urshl v23.16b, v23.16b, v28.16b \n \t "
" sub v20.16b, v20.16b, v24.16b \n \t "
" sub v21.16b, v21.16b, v25.16b \n \t "
" sub v22.16b, v22.16b, v26.16b \n \t "
" sub v23.16b, v23.16b, v27.16b \n \t "
" add v20.16b, v20.16b, v16.16b \n \t "
" add v21.16b, v21.16b, v17.16b \n \t "
" add v22.16b, v22.16b, v18.16b \n \t "
" add v23.16b, v23.16b, v19.16b \n \t "
" stp q20, q21, [%2], #32 \n \t "
" stp q22, q23, [%2], #32 \n \t "
" subs %3, %3, #64 \n \t "
2019-09-03 23:55:37 +08:00
" bne neon64_armv8_fastblend_iter%= \n \t "
2017-05-13 03:59:08 +08:00
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count ) , " r " ( divider )
: " %x12 " , " %v16 " , " %v17 " , " %v18 " , " %v19 " , " %v20 " , " %v21 " , " %v22 " , " %v23 " , " %v24 " , " %v25 " , " %v26 " , " %v27 " , " %v28 " , " cc " , " memory "
) ;
2017-03-19 19:39:24 +08:00
# else
2017-03-26 02:49:15 +08:00
Panic ( " Neon function called on a non-ARM platform or Neon code is absent " ) ;
2017-03-19 19:39:24 +08:00
# endif
2017-03-19 19:31:44 +08:00
}
2013-03-17 07:45:21 +08:00
__attribute__ ( ( noinline ) ) void std_blend ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count , double blendpercent ) {
2016-04-04 22:11:48 +08:00
double divide = blendpercent / 100.0 ;
double opacity = 1.0 - divide ;
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2018-10-22 22:41:27 +08:00
while ( result < max_ptr ) {
2016-04-04 22:11:48 +08:00
* result + + = ( * col1 + + * opacity ) + ( * col2 + + * divide ) ;
2019-09-17 22:31:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/************************************************* DELTA FUNCTIONS *************************************************/
/* Grayscale */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_delta8_gray8 ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
/* Loop unrolling is used to work on 16 bytes (16 grayscale pixels) at a time */
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
result [ 0 ] = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
result [ 1 ] = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
result [ 2 ] = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 3 ] = abs ( col1 [ 3 ] - col2 [ 3 ] ) ;
result [ 4 ] = abs ( col1 [ 4 ] - col2 [ 4 ] ) ;
result [ 5 ] = abs ( col1 [ 5 ] - col2 [ 5 ] ) ;
result [ 6 ] = abs ( col1 [ 6 ] - col2 [ 6 ] ) ;
result [ 7 ] = abs ( col1 [ 7 ] - col2 [ 7 ] ) ;
result [ 8 ] = abs ( col1 [ 8 ] - col2 [ 8 ] ) ;
result [ 9 ] = abs ( col1 [ 9 ] - col2 [ 9 ] ) ;
result [ 10 ] = abs ( col1 [ 10 ] - col2 [ 10 ] ) ;
result [ 11 ] = abs ( col1 [ 11 ] - col2 [ 11 ] ) ;
result [ 12 ] = abs ( col1 [ 12 ] - col2 [ 12 ] ) ;
result [ 13 ] = abs ( col1 [ 13 ] - col2 [ 13 ] ) ;
result [ 14 ] = abs ( col1 [ 14 ] - col2 [ 14 ] ) ;
result [ 15 ] = abs ( col1 [ 15 ] - col2 [ 15 ] ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
col2 + = 16 ;
result + = 16 ;
2019-09-17 22:31:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_delta8_gray8 ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
result [ 0 ] = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
col1 + = 1 ;
col2 + = 1 ;
result + = 1 ;
2019-09-17 22:31:48 +08:00
}
2019-02-22 22:44:57 +08:00
}
2013-03-17 07:45:21 +08:00
/* RGB24: RGB */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_delta8_rgb ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
/* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
r = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
g = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
b = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 3 ] - col2 [ 3 ] ) ;
g = abs ( col1 [ 4 ] - col2 [ 4 ] ) ;
b = abs ( col1 [ 5 ] - col2 [ 5 ] ) ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 6 ] - col2 [ 6 ] ) ;
g = abs ( col1 [ 7 ] - col2 [ 7 ] ) ;
b = abs ( col1 [ 8 ] - col2 [ 8 ] ) ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 9 ] - col2 [ 9 ] ) ;
g = abs ( col1 [ 10 ] - col2 [ 10 ] ) ;
b = abs ( col1 [ 11 ] - col2 [ 11 ] ) ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 12 ;
col2 + = 12 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_delta8_rgb ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
/* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
r = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
g = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
b = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 3 ;
col2 + = 3 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* RGB24: BGR */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_delta8_bgr ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
/* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2016-04-04 22:11:48 +08:00
b = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
g = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
r = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 3 ] - col2 [ 3 ] ) ;
g = abs ( col1 [ 4 ] - col2 [ 4 ] ) ;
r = abs ( col1 [ 5 ] - col2 [ 5 ] ) ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 6 ] - col2 [ 6 ] ) ;
g = abs ( col1 [ 7 ] - col2 [ 7 ] ) ;
r = abs ( col1 [ 8 ] - col2 [ 8 ] ) ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 9 ] - col2 [ 9 ] ) ;
g = abs ( col1 [ 10 ] - col2 [ 10 ] ) ;
r = abs ( col1 [ 11 ] - col2 [ 11 ] ) ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 12 ;
col2 + = 12 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_delta8_bgr ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
/* Loop unrolling is used to work on 12 bytes (4 rgb24 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2019-02-22 22:44:57 +08:00
b = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
g = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
r = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 3 ;
col2 + = 3 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* RGB32: RGBA */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_delta8_rgba ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
/* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2016-04-04 22:11:48 +08:00
r = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
g = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
b = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 4 ] - col2 [ 4 ] ) ;
g = abs ( col1 [ 5 ] - col2 [ 5 ] ) ;
b = abs ( col1 [ 6 ] - col2 [ 6 ] ) ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 8 ] - col2 [ 8 ] ) ;
g = abs ( col1 [ 9 ] - col2 [ 9 ] ) ;
b = abs ( col1 [ 10 ] - col2 [ 10 ] ) ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 12 ] - col2 [ 12 ] ) ;
g = abs ( col1 [ 13 ] - col2 [ 13 ] ) ;
b = abs ( col1 [ 14 ] - col2 [ 14 ] ) ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
col2 + = 16 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_delta8_rgba ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
/* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2019-02-22 22:44:57 +08:00
r = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
g = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
b = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 4 ;
col2 + = 4 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* RGB32: BGRA */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_delta8_bgra ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
/* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2016-04-04 22:11:48 +08:00
b = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
g = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
r = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 4 ] - col2 [ 4 ] ) ;
g = abs ( col1 [ 5 ] - col2 [ 5 ] ) ;
r = abs ( col1 [ 6 ] - col2 [ 6 ] ) ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 8 ] - col2 [ 8 ] ) ;
g = abs ( col1 [ 9 ] - col2 [ 9 ] ) ;
r = abs ( col1 [ 10 ] - col2 [ 10 ] ) ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 12 ] - col2 [ 12 ] ) ;
g = abs ( col1 [ 13 ] - col2 [ 13 ] ) ;
r = abs ( col1 [ 14 ] - col2 [ 14 ] ) ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
col2 + = 16 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_delta8_bgra ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
/* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2019-02-22 22:44:57 +08:00
b = abs ( col1 [ 0 ] - col2 [ 0 ] ) ;
g = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
r = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 4 ;
col2 + = 4 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* RGB32: ARGB */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_delta8_argb ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
/* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2016-04-04 22:11:48 +08:00
r = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
g = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
b = abs ( col1 [ 3 ] - col2 [ 3 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 5 ] - col2 [ 5 ] ) ;
g = abs ( col1 [ 6 ] - col2 [ 6 ] ) ;
b = abs ( col1 [ 7 ] - col2 [ 7 ] ) ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 9 ] - col2 [ 9 ] ) ;
g = abs ( col1 [ 10 ] - col2 [ 10 ] ) ;
b = abs ( col1 [ 11 ] - col2 [ 11 ] ) ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( col1 [ 13 ] - col2 [ 13 ] ) ;
g = abs ( col1 [ 14 ] - col2 [ 14 ] ) ;
b = abs ( col1 [ 15 ] - col2 [ 15 ] ) ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
col2 + = 16 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2020-07-23 05:30:28 +08:00
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_delta8_argb ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
/* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2019-02-22 22:44:57 +08:00
r = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
g = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
b = abs ( col1 [ 3 ] - col2 [ 3 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 4 ;
col2 + = 4 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* RGB32: ABGR */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_delta8_abgr ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
/* Loop unrolling is used to work on 16 bytes (4 rgb32 pixels) at a time */
2019-09-17 22:31:48 +08:00
int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2016-04-04 22:11:48 +08:00
b = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
g = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
r = abs ( col1 [ 3 ] - col2 [ 3 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 5 ] - col2 [ 5 ] ) ;
g = abs ( col1 [ 6 ] - col2 [ 6 ] ) ;
r = abs ( col1 [ 7 ] - col2 [ 7 ] ) ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 9 ] - col2 [ 9 ] ) ;
g = abs ( col1 [ 10 ] - col2 [ 10 ] ) ;
r = abs ( col1 [ 11 ] - col2 [ 11 ] ) ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( col1 [ 13 ] - col2 [ 13 ] ) ;
g = abs ( col1 [ 14 ] - col2 [ 14 ] ) ;
r = abs ( col1 [ 15 ] - col2 [ 15 ] ) ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
col2 + = 16 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_delta8_abgr ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
2020-07-23 05:30:28 +08:00
while ( result < max_ptr ) {
2019-02-22 22:44:57 +08:00
b = abs ( col1 [ 1 ] - col2 [ 1 ] ) ;
g = abs ( col1 [ 2 ] - col2 [ 2 ] ) ;
r = abs ( col1 [ 3 ] - col2 [ 3 ] ) ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 4 ;
col2 + = 4 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
2017-03-19 19:02:42 +08:00
/* Grayscale Neon for AArch32 */
2018-10-12 22:34:16 +08:00
# if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON))
2017-03-19 19:02:42 +08:00
__attribute__ ( ( noinline , __target__ ( " fpu=neon " ) ) )
# endif
2017-03-19 19:39:24 +08:00
void neon32_armv7_delta8_gray8 ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2018-10-12 22:34:16 +08:00
# if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON))
2017-03-19 19:02:42 +08:00
2017-04-16 15:57:37 +08:00
/* Q0(D0,D1) = col1+0 */
/* Q1(D2,D3) = col1+16 */
/* Q2(D4,D5) = col1+32 */
/* Q3(D6,D7) = col1+48 */
/* Q4(D8,D9) = col2+0 */
/* Q5(D10,D11) = col2+16 */
/* Q6(D12,D13) = col2+32 */
/* Q7(D14,D15) = col2+48 */
2017-03-19 19:02:42 +08:00
__asm__ __volatile__ (
2019-09-03 23:55:37 +08:00
" neon32_armv7_delta8_gray8_iter%=: \n \t "
2017-04-16 15:57:37 +08:00
" vldm %0!, {q0,q1,q2,q3} \n \t "
" vldm %1!, {q4,q5,q6,q7} \n \t "
2017-05-14 00:08:18 +08:00
" pld [%0, #512] \n \t "
" pld [%1, #512] \n \t "
2017-04-16 15:57:37 +08:00
" vabd.u8 q0, q0, q4 \n \t "
" vabd.u8 q1, q1, q5 \n \t "
" vabd.u8 q2, q2, q6 \n \t "
" vabd.u8 q3, q3, q7 \n \t "
" vstm %2!, {q0,q1,q2,q3} \n \t "
" subs %3, %3, #64 \n \t "
2019-09-03 23:55:37 +08:00
" bne neon32_armv7_delta8_gray8_iter%= \n \t "
2017-03-19 19:02:42 +08:00
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count )
2017-04-16 15:57:37 +08:00
: " %q0 " , " %q1 " , " %q2 " , " %q3 " , " %q4 " , " %q5 " , " %q6 " , " %q7 " , " cc " , " memory "
2017-03-19 19:02:42 +08:00
) ;
# else
2017-03-26 02:49:15 +08:00
Panic ( " Neon function called on a non-ARM platform or Neon code is absent " ) ;
2017-03-19 19:02:42 +08:00
# endif
}
2017-05-13 14:19:13 +08:00
/* Grayscale Neon for AArch64 */
__attribute__ ( ( noinline ) ) void neon64_armv8_delta8_gray8 ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
# if (defined(__aarch64__) && !defined(ZM_STRIP_NEON))
/* V16 = col1+0 */
/* V17 = col1+16 */
/* V18 = col1+32 */
/* V19 = col1+48 */
/* V20 = col2+0 */
/* V21 = col2+16 */
/* V22 = col2+32 */
/* V23 = col2+48 */
__asm__ __volatile__ (
2019-09-03 23:55:37 +08:00
" neon64_armv8_delta8_gray8_iter%=: \n \t "
2017-05-13 14:19:13 +08:00
" ldp q16, q17, [%0], #32 \n \t "
" ldp q18, q19, [%0], #32 \n \t "
" ldp q20, q21, [%1], #32 \n \t "
" ldp q22, q23, [%1], #32 \n \t "
2017-05-14 00:08:18 +08:00
" prfm pldl1keep, [%0, #512] \n \t "
" prfm pldl1keep, [%1, #512] \n \t "
2017-05-13 14:19:13 +08:00
" uabd v16.16b, v16.16b, v20.16b \n \t "
" uabd v17.16b, v17.16b, v21.16b \n \t "
" uabd v18.16b, v18.16b, v22.16b \n \t "
" uabd v19.16b, v19.16b, v23.16b \n \t "
" stp q16, q17, [%2], #32 \n \t "
" stp q18, q19, [%2], #32 \n \t "
" subs %3, %3, #64 \n \t "
2019-09-03 23:55:37 +08:00
" bne neon64_armv8_delta8_gray8_iter%= \n \t "
2017-05-13 14:19:13 +08:00
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count )
: " %v16 " , " %v17 " , " %v18 " , " %v19 " , " %v20 " , " %v21 " , " %v22 " , " %v23 " , " cc " , " memory "
) ;
2017-03-19 19:02:42 +08:00
# else
2017-03-26 02:49:15 +08:00
Panic ( " Neon function called on a non-ARM platform or Neon code is absent " ) ;
2017-03-19 19:02:42 +08:00
# endif
}
/* RGB32 Neon for AArch32 */
2018-10-12 22:34:16 +08:00
# if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON))
2017-03-19 19:02:42 +08:00
__attribute__ ( ( noinline , __target__ ( " fpu=neon " ) ) )
# endif
2017-03-19 19:39:24 +08:00
void neon32_armv7_delta8_rgb32 ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count , uint32_t multiplier ) {
2018-10-12 22:34:16 +08:00
# if (defined(__arm__) && defined(__ARM_PCS_VFP) && !defined(ZM_STRIP_NEON))
2017-03-19 19:02:42 +08:00
2017-04-16 15:57:37 +08:00
/* Q0(D0,D1) = col1+0 */
/* Q1(D2,D3) = col1+16 */
/* Q2(D4,D5) = col1+32 */
/* Q3(D6,D7) = col1+48 */
/* Q4(D8,D9) = col2+0 */
/* Q5(D10,D11) = col2+16 */
/* Q6(D12,D13) = col2+32 */
/* Q7(D14,D15) = col2+48 */
/* Q8(D16,D17) = multiplier */
2017-03-19 19:02:42 +08:00
__asm__ __volatile__ (
" mov r12, %4 \n \t "
2017-04-16 15:57:37 +08:00
" vdup.32 q8, r12 \n \t "
2019-09-03 23:55:37 +08:00
" neon32_armv7_delta8_rgb32_iter%=: \n \t "
2017-04-16 15:57:37 +08:00
" vldm %0!, {q0,q1,q2,q3} \n \t "
" vldm %1!, {q4,q5,q6,q7} \n \t "
2017-05-14 00:08:18 +08:00
" pld [%0, #256] \n \t "
" pld [%1, #256] \n \t "
2017-04-16 15:57:37 +08:00
" vabd.u8 q0, q0, q4 \n \t "
" vabd.u8 q1, q1, q5 \n \t "
" vabd.u8 q2, q2, q6 \n \t "
" vabd.u8 q3, q3, q7 \n \t "
2017-03-19 19:02:42 +08:00
" vrshr.u8 q0, q0, #3 \n \t "
2017-04-16 15:57:37 +08:00
" vrshr.u8 q1, q1, #3 \n \t "
" vrshr.u8 q2, q2, #3 \n \t "
" vrshr.u8 q3, q3, #3 \n \t "
" vmul.i8 q0, q0, q8 \n \t "
" vmul.i8 q1, q1, q8 \n \t "
" vmul.i8 q2, q2, q8 \n \t "
" vmul.i8 q3, q3, q8 \n \t "
2017-03-19 19:02:42 +08:00
" vpadd.i8 d0, d0, d1 \n \t "
" vpadd.i8 d2, d2, d3 \n \t "
2017-04-16 15:57:37 +08:00
" vpadd.i8 d4, d4, d5 \n \t "
" vpadd.i8 d6, d6, d7 \n \t "
" vpadd.i8 d0, d0, d0 \n \t "
" vpadd.i8 d1, d2, d2 \n \t "
" vpadd.i8 d2, d4, d4 \n \t "
" vpadd.i8 d3, d6, d6 \n \t "
" vst4.32 {d0[0],d1[0],d2[0],d3[0]}, [%2]! \n \t "
" subs %3, %3, #16 \n \t "
2019-09-03 23:55:37 +08:00
" bne neon32_armv7_delta8_rgb32_iter%= \n \t "
2017-03-19 19:02:42 +08:00
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count ) , " r " ( multiplier )
2017-04-16 15:57:37 +08:00
: " %r12 " , " %q0 " , " %q1 " , " %q2 " , " %q3 " , " %q4 " , " %q5 " , " %q6 " , " %q7 " , " %q8 " , " cc " , " memory "
2017-03-19 19:02:42 +08:00
) ;
# else
2017-03-26 02:49:15 +08:00
Panic ( " Neon function called on a non-ARM platform or Neon code is absent " ) ;
2017-03-19 19:02:42 +08:00
# endif
2017-04-16 15:57:37 +08:00
}
2017-03-19 19:02:42 +08:00
2017-05-13 14:19:13 +08:00
/* RGB32 Neon for AArch64 */
__attribute__ ( ( noinline ) ) void neon64_armv8_delta8_rgb32 ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count , uint32_t multiplier ) {
# if (defined(__aarch64__) && !defined(ZM_STRIP_NEON))
/* V16 = col1+0 */
/* V17 = col1+16 */
/* V18 = col1+32 */
/* V19 = col1+48 */
/* V20 = col2+0 */
/* V21 = col2+16 */
/* V22 = col2+32 */
/* V23 = col2+48 */
/* V24 = multiplier */
__asm__ __volatile__ (
" mov x12, %4 \n \t "
" dup v24.4s, w12 \n \t "
2019-09-03 23:55:37 +08:00
" neon64_armv8_delta8_rgb32_iter%=: \n \t "
2017-05-13 14:19:13 +08:00
" ldp q16, q17, [%0], #32 \n \t "
" ldp q18, q19, [%0], #32 \n \t "
" ldp q20, q21, [%1], #32 \n \t "
" ldp q22, q23, [%1], #32 \n \t "
2017-05-14 00:08:18 +08:00
" prfm pldl1keep, [%0, #256] \n \t "
" prfm pldl1keep, [%1, #256] \n \t "
2017-05-13 14:19:13 +08:00
" uabd v16.16b, v16.16b, v20.16b \n \t "
" uabd v17.16b, v17.16b, v21.16b \n \t "
" uabd v18.16b, v18.16b, v22.16b \n \t "
" uabd v19.16b, v19.16b, v23.16b \n \t "
" urshr v16.16b, v16.16b, #3 \n \t "
" urshr v17.16b, v17.16b, #3 \n \t "
" urshr v18.16b, v18.16b, #3 \n \t "
" urshr v19.16b, v19.16b, #3 \n \t "
" mul v16.16b, v16.16b, v24.16b \n \t "
" mul v17.16b, v17.16b, v24.16b \n \t "
" mul v18.16b, v18.16b, v24.16b \n \t "
" mul v19.16b, v19.16b, v24.16b \n \t "
" addp v16.16b, v16.16b, v16.16b \n \t "
" addp v17.16b, v17.16b, v17.16b \n \t "
" addp v18.16b, v18.16b, v18.16b \n \t "
" addp v19.16b, v19.16b, v19.16b \n \t "
" addp v16.16b, v16.16b, v16.16b \n \t "
" addp v17.16b, v17.16b, v17.16b \n \t "
" addp v18.16b, v18.16b, v18.16b \n \t "
" addp v19.16b, v19.16b, v19.16b \n \t "
" st4 {v16.s, v17.s, v18.s, v19.s}[0], [%2], #16 \n \t "
" subs %3, %3, #16 \n \t "
2019-09-03 23:55:37 +08:00
" bne neon64_armv8_delta8_rgb32_iter%= \n \t "
2017-05-13 14:19:13 +08:00
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count ) , " r " ( multiplier )
: " %x12 " , " %v16 " , " %v17 " , " %v18 " , " %v19 " , " %v20 " , " %v21 " , " %v22 " , " %v23 " , " %v24 " , " cc " , " memory "
) ;
2017-03-19 19:02:42 +08:00
# else
2017-03-26 02:49:15 +08:00
Panic ( " Neon function called on a non-ARM platform or Neon code is absent " ) ;
2017-03-19 19:02:42 +08:00
# endif
}
/* RGB32: RGBA Neon for AArch32 */
2017-03-19 19:39:24 +08:00
void neon32_armv7_delta8_rgba ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2017-03-19 19:02:42 +08:00
neon32_armv7_delta8_rgb32 ( col1 , col2 , result , count , 0x00010502 ) ;
}
/* RGB32: BGRA Neon for AArch32 */
2017-03-19 19:39:24 +08:00
void neon32_armv7_delta8_bgra ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2017-03-19 19:02:42 +08:00
neon32_armv7_delta8_rgb32 ( col1 , col2 , result , count , 0x00020501 ) ;
}
/* RGB32: ARGB Neon for AArch32 */
2017-03-19 19:39:24 +08:00
void neon32_armv7_delta8_argb ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2017-03-19 19:02:42 +08:00
neon32_armv7_delta8_rgb32 ( col1 , col2 , result , count , 0x01050200 ) ;
}
/* RGB32: ABGR Neon for AArch32 */
2017-03-19 19:39:24 +08:00
void neon32_armv7_delta8_abgr ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2017-03-19 19:02:42 +08:00
neon32_armv7_delta8_rgb32 ( col1 , col2 , result , count , 0x02050100 ) ;
}
2017-05-13 14:19:13 +08:00
/* RGB32: RGBA Neon for AArch64 */
void neon64_armv8_delta8_rgba ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
neon64_armv8_delta8_rgb32 ( col1 , col2 , result , count , 0x00010502 ) ;
}
/* RGB32: BGRA Neon for AArch64 */
void neon64_armv8_delta8_bgra ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
neon64_armv8_delta8_rgb32 ( col1 , col2 , result , count , 0x00020501 ) ;
}
/* RGB32: ARGB Neon for AArch64 */
void neon64_armv8_delta8_argb ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
neon64_armv8_delta8_rgb32 ( col1 , col2 , result , count , 0x01050200 ) ;
}
/* RGB32: ABGR Neon for AArch64 */
void neon64_armv8_delta8_abgr ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
neon64_armv8_delta8_rgb32 ( col1 , col2 , result , count , 0x02050100 ) ;
}
2013-03-17 07:45:21 +08:00
/* Grayscale SSE2 */
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " sse2 " ) ) )
# endif
void sse2_delta8_gray8 ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2013-03-17 07:45:21 +08:00
2016-04-04 22:11:48 +08:00
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" sub $0x10, %0 \n \t "
" sub $0x10, %1 \n \t "
" sub $0x10, %2 \n \t "
" sse2_delta8_gray8_iter: \n \t "
" movdqa (%0,%3), %%xmm1 \n \t "
" movdqa (%1,%3), %%xmm2 \n \t "
" movdqa %%xmm1, %%xmm3 \n \t "
" movdqa %%xmm2, %%xmm4 \n \t "
" pmaxub %%xmm1, %%xmm2 \n \t "
" pminub %%xmm3, %%xmm4 \n \t "
" psubb %%xmm4, %%xmm2 \n \t "
" movntdq %%xmm2, (%2,%3) \n \t "
" sub $0x10, %3 \n \t "
" jnz sse2_delta8_gray8_iter \n \t "
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count )
: " %xmm1 " , " %xmm2 " , " %xmm3 " , " %xmm4 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
/* RGB32: RGBA SSE2 */
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " sse2 " ) ) )
# endif
void sse2_delta8_rgba ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" mov $0x1F1F1F1F, %%eax \n \t "
" movd %%eax, %%xmm4 \n \t "
" pshufd $0x0, %%xmm4, %%xmm4 \n \t "
" mov $0xff, %%eax \n \t "
" movd %%eax, %%xmm0 \n \t "
" pshufd $0x0, %%xmm0, %%xmm0 \n \t "
" sub $0x10, %0 \n \t "
" sub $0x10, %1 \n \t "
" sub $0x4, %2 \n \t "
" sse2_delta8_rgba_iter: \n \t "
" movdqa (%0,%3,4), %%xmm1 \n \t "
" movdqa (%1,%3,4), %%xmm2 \n \t "
" psrlq $0x3, %%xmm1 \n \t "
" psrlq $0x3, %%xmm2 \n \t "
" pand %%xmm4, %%xmm1 \n \t "
" pand %%xmm4, %%xmm2 \n \t "
" movdqa %%xmm1, %%xmm5 \n \t "
" movdqa %%xmm2, %%xmm6 \n \t "
" pmaxub %%xmm1, %%xmm2 \n \t "
" pminub %%xmm5, %%xmm6 \n \t "
" psubb %%xmm6, %%xmm2 \n \t "
" movdqa %%xmm2, %%xmm3 \n \t "
" psrld $0x8, %%xmm2 \n \t "
" pand %%xmm0, %%xmm2 \n \t "
" movdqa %%xmm2, %%xmm1 \n \t "
" pslld $0x2, %%xmm2 \n \t "
" paddd %%xmm1, %%xmm2 \n \t "
" movdqa %%xmm3, %%xmm1 \n \t "
" pand %%xmm0, %%xmm1 \n \t "
" paddd %%xmm1, %%xmm1 \n \t "
" paddd %%xmm2, %%xmm1 \n \t "
" movdqa %%xmm3, %%xmm2 \n \t "
" psrld $0x10, %%xmm2 \n \t "
" pand %%xmm0, %%xmm2 \n \t "
" paddd %%xmm2, %%xmm1 \n \t "
" packssdw %%xmm1, %%xmm1 \n \t "
" packuswb %%xmm1, %%xmm1 \n \t "
" movd %%xmm1, %%eax \n \t "
" movnti %%eax, (%2,%3) \n \t "
" sub $0x4, %3 \n \t "
" jnz sse2_delta8_rgba_iter \n \t "
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count )
: " %eax " , " %xmm0 " , " %xmm1 " , " %xmm2 " , " %xmm3 " , " %xmm4 " , " %xmm5 " , " %xmm6 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
/* RGB32: BGRA SSE2 */
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " sse2 " ) ) )
# endif
void sse2_delta8_bgra ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" mov $0x1F1F1F1F, %%eax \n \t "
" movd %%eax, %%xmm4 \n \t "
" pshufd $0x0, %%xmm4, %%xmm4 \n \t "
" mov $0xff, %%eax \n \t "
" movd %%eax, %%xmm0 \n \t "
" pshufd $0x0, %%xmm0, %%xmm0 \n \t "
" sub $0x10, %0 \n \t "
" sub $0x10, %1 \n \t "
" sub $0x4, %2 \n \t "
" sse2_delta8_bgra_iter: \n \t "
" movdqa (%0,%3,4), %%xmm1 \n \t "
" movdqa (%1,%3,4), %%xmm2 \n \t "
" psrlq $0x3, %%xmm1 \n \t "
" psrlq $0x3, %%xmm2 \n \t "
" pand %%xmm4, %%xmm1 \n \t "
" pand %%xmm4, %%xmm2 \n \t "
" movdqa %%xmm1, %%xmm5 \n \t "
" movdqa %%xmm2, %%xmm6 \n \t "
" pmaxub %%xmm1, %%xmm2 \n \t "
" pminub %%xmm5, %%xmm6 \n \t "
" psubb %%xmm6, %%xmm2 \n \t "
" movdqa %%xmm2, %%xmm3 \n \t "
" psrld $0x8, %%xmm2 \n \t "
" pand %%xmm0, %%xmm2 \n \t "
" movdqa %%xmm2, %%xmm1 \n \t "
" pslld $0x2, %%xmm2 \n \t "
" paddd %%xmm1, %%xmm2 \n \t "
" movdqa %%xmm3, %%xmm1 \n \t "
" pand %%xmm0, %%xmm1 \n \t "
" paddd %%xmm2, %%xmm1 \n \t "
" movdqa %%xmm3, %%xmm2 \n \t "
" psrld $0x10, %%xmm2 \n \t "
" pand %%xmm0, %%xmm2 \n \t "
" paddd %%xmm2, %%xmm2 \n \t "
" paddd %%xmm2, %%xmm1 \n \t "
" packssdw %%xmm1, %%xmm1 \n \t "
" packuswb %%xmm1, %%xmm1 \n \t "
" movd %%xmm1, %%eax \n \t "
" movnti %%eax, (%2,%3) \n \t "
" sub $0x4, %3 \n \t "
" jnz sse2_delta8_bgra_iter \n \t "
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count )
: " %eax " , " %xmm0 " , " %xmm1 " , " %xmm2 " , " %xmm3 " , " %xmm4 " , " %xmm5 " , " %xmm6 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
/* RGB32: ARGB SSE2 */
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " sse2 " ) ) )
# endif
void sse2_delta8_argb ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" mov $0x1F1F1F1F, %%eax \n \t "
" movd %%eax, %%xmm4 \n \t "
" pshufd $0x0, %%xmm4, %%xmm4 \n \t "
" mov $0xff, %%eax \n \t "
" movd %%eax, %%xmm0 \n \t "
" pshufd $0x0, %%xmm0, %%xmm0 \n \t "
" sub $0x10, %0 \n \t "
" sub $0x10, %1 \n \t "
" sub $0x4, %2 \n \t "
" sse2_delta8_argb_iter: \n \t "
" movdqa (%0,%3,4), %%xmm1 \n \t "
" movdqa (%1,%3,4), %%xmm2 \n \t "
" psrlq $0x3, %%xmm1 \n \t "
" psrlq $0x3, %%xmm2 \n \t "
" pand %%xmm4, %%xmm1 \n \t "
" pand %%xmm4, %%xmm2 \n \t "
" movdqa %%xmm1, %%xmm5 \n \t "
" movdqa %%xmm2, %%xmm6 \n \t "
" pmaxub %%xmm1, %%xmm2 \n \t "
" pminub %%xmm5, %%xmm6 \n \t "
" psubb %%xmm6, %%xmm2 \n \t "
" movdqa %%xmm2, %%xmm3 \n \t "
" psrld $0x10, %%xmm2 \n \t "
" pand %%xmm0, %%xmm2 \n \t "
" movdqa %%xmm2, %%xmm1 \n \t "
" pslld $0x2, %%xmm2 \n \t "
" paddd %%xmm1, %%xmm2 \n \t "
" movdqa %%xmm3, %%xmm1 \n \t "
" psrld $0x8, %%xmm1 \n \t "
" pand %%xmm0, %%xmm1 \n \t "
" paddd %%xmm1, %%xmm1 \n \t "
" paddd %%xmm2, %%xmm1 \n \t "
" movdqa %%xmm3, %%xmm2 \n \t "
" psrld $0x18, %%xmm2 \n \t "
" pand %%xmm0, %%xmm2 \n \t "
" paddd %%xmm2, %%xmm1 \n \t "
" packssdw %%xmm1, %%xmm1 \n \t "
" packuswb %%xmm1, %%xmm1 \n \t "
" movd %%xmm1, %%eax \n \t "
" movnti %%eax, (%2,%3) \n \t "
" sub $0x4, %3 \n \t "
" jnz sse2_delta8_argb_iter \n \t "
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count )
: " %eax " , " %xmm0 " , " %xmm1 " , " %xmm2 " , " %xmm3 " , " %xmm4 " , " %xmm5 " , " %xmm6 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
/* RGB32: ABGR SSE2 */
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " sse2 " ) ) )
# endif
void sse2_delta8_abgr ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" mov $0x1F1F1F1F, %%eax \n \t "
" movd %%eax, %%xmm4 \n \t "
" pshufd $0x0, %%xmm4, %%xmm4 \n \t "
" mov $0xff, %%eax \n \t "
" movd %%eax, %%xmm0 \n \t "
" pshufd $0x0, %%xmm0, %%xmm0 \n \t "
" sub $0x10, %0 \n \t "
" sub $0x10, %1 \n \t "
" sub $0x4, %2 \n \t "
" sse2_delta8_abgr_iter: \n \t "
" movdqa (%0,%3,4), %%xmm1 \n \t "
" movdqa (%1,%3,4), %%xmm2 \n \t "
" psrlq $0x3, %%xmm1 \n \t "
" psrlq $0x3, %%xmm2 \n \t "
" pand %%xmm4, %%xmm1 \n \t "
" pand %%xmm4, %%xmm2 \n \t "
" movdqa %%xmm1, %%xmm5 \n \t "
" movdqa %%xmm2, %%xmm6 \n \t "
" pmaxub %%xmm1, %%xmm2 \n \t "
" pminub %%xmm5, %%xmm6 \n \t "
" psubb %%xmm6, %%xmm2 \n \t "
" movdqa %%xmm2, %%xmm3 \n \t "
" psrld $0x10, %%xmm2 \n \t "
" pand %%xmm0, %%xmm2 \n \t "
" movdqa %%xmm2, %%xmm1 \n \t "
" pslld $0x2, %%xmm2 \n \t "
" paddd %%xmm1, %%xmm2 \n \t "
" movdqa %%xmm3, %%xmm1 \n \t "
" psrld $0x8, %%xmm1 \n \t "
" pand %%xmm0, %%xmm1 \n \t "
" paddd %%xmm2, %%xmm1 \n \t "
" movdqa %%xmm3, %%xmm2 \n \t "
" psrld $0x18, %%xmm2 \n \t "
" pand %%xmm0, %%xmm2 \n \t "
" paddd %%xmm2, %%xmm2 \n \t "
" paddd %%xmm2, %%xmm1 \n \t "
" packssdw %%xmm1, %%xmm1 \n \t "
" packuswb %%xmm1, %%xmm1 \n \t "
" movd %%xmm1, %%eax \n \t "
" movnti %%eax, (%2,%3) \n \t "
" sub $0x4, %3 \n \t "
" jnz sse2_delta8_abgr_iter \n \t "
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count )
: " %eax " , " %xmm0 " , " %xmm1 " , " %xmm2 " , " %xmm3 " , " %xmm4 " , " %xmm5 " , " %xmm6 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
2017-03-22 17:38:15 +08:00
/* RGB32 SSSE3 */
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " ssse3 " ) ) )
# endif
2017-03-22 17:38:15 +08:00
void ssse3_delta8_rgb32 ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count , uint32_t multiplier ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2017-03-22 17:38:15 +08:00
/* XMM0 - zero */
/* XMM1 - col1 */
/* XMM2 - col2 */
/* XMM3 - multiplier */
/* XMM4 - divide mask */
2017-03-14 18:17:01 +08:00
2016-04-04 22:11:48 +08:00
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" mov $0x1F1F1F1F, %%eax \n \t "
" movd %%eax, %%xmm4 \n \t "
" pshufd $0x0, %%xmm4, %%xmm4 \n \t "
" mov %4, %%eax \n \t "
" movd %%eax, %%xmm3 \n \t "
" pshufd $0x0, %%xmm3, %%xmm3 \n \t "
" pxor %%xmm0, %%xmm0 \n \t "
" sub $0x10, %0 \n \t "
" sub $0x10, %1 \n \t "
" sub $0x4, %2 \n \t "
" ssse3_delta8_rgb32_iter: \n \t "
" movdqa (%0,%3,4), %%xmm1 \n \t "
" movdqa (%1,%3,4), %%xmm2 \n \t "
" psrlq $0x3, %%xmm1 \n \t "
" psrlq $0x3, %%xmm2 \n \t "
" pand %%xmm4, %%xmm1 \n \t "
" pand %%xmm4, %%xmm2 \n \t "
" psubb %%xmm2, %%xmm1 \n \t "
" pabsb %%xmm1, %%xmm1 \n \t "
" pmaddubsw %%xmm3, %%xmm1 \n \t "
" phaddw %%xmm0, %%xmm1 \n \t "
" packuswb %%xmm1, %%xmm1 \n \t "
" movd %%xmm1, %%eax \n \t "
" movnti %%eax, (%2,%3) \n \t "
" sub $0x4, %3 \n \t "
" jnz ssse3_delta8_rgb32_iter \n \t "
:
: " r " ( col1 ) , " r " ( col2 ) , " r " ( result ) , " r " ( count ) , " g " ( multiplier )
: " %eax " , " %xmm0 " , " %xmm1 " , " %xmm2 " , " %xmm3 " , " %xmm4 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
2017-03-22 17:38:15 +08:00
/* RGB32: RGBA SSSE3 */
void ssse3_delta8_rgba ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
ssse3_delta8_rgb32 ( col1 , col2 , result , count , 0x00010502 ) ;
}
2013-03-17 07:45:21 +08:00
/* RGB32: BGRA SSSE3 */
2014-03-16 23:51:54 +08:00
void ssse3_delta8_bgra ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2017-03-22 17:38:15 +08:00
ssse3_delta8_rgb32 ( col1 , col2 , result , count , 0x00020501 ) ;
2013-03-17 07:45:21 +08:00
}
/* RGB32: ARGB SSSE3 */
2014-03-16 23:51:54 +08:00
void ssse3_delta8_argb ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2017-03-22 17:38:15 +08:00
ssse3_delta8_rgb32 ( col1 , col2 , result , count , 0x01050200 ) ;
2013-03-17 07:45:21 +08:00
}
/* RGB32: ABGR SSSE3 */
2014-03-16 23:51:54 +08:00
void ssse3_delta8_abgr ( const uint8_t * col1 , const uint8_t * col2 , uint8_t * result , unsigned long count ) {
2017-03-22 17:38:15 +08:00
ssse3_delta8_rgb32 ( col1 , col2 , result , count , 0x02050100 ) ;
2013-03-17 07:45:21 +08:00
}
/************************************************* CONVERT FUNCTIONS *************************************************/
/* RGB24 to grayscale */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_convert_rgb_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
r = col1 [ 0 ] ;
g = col1 [ 1 ] ;
b = col1 [ 2 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 3 ] ;
g = col1 [ 4 ] ;
b = col1 [ 5 ] ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 6 ] ;
g = col1 [ 7 ] ;
b = col1 [ 8 ] ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 9 ] ;
g = col1 [ 10 ] ;
b = col1 [ 11 ] ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 12 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_convert_rgb_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
r = col1 [ 0 ] ;
g = col1 [ 1 ] ;
b = col1 [ 2 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 3 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* BGR24 to grayscale */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_convert_bgr_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
b = col1 [ 0 ] ;
g = col1 [ 1 ] ;
r = col1 [ 2 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 3 ] ;
g = col1 [ 4 ] ;
r = col1 [ 5 ] ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 6 ] ;
g = col1 [ 7 ] ;
r = col1 [ 8 ] ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 9 ] ;
g = col1 [ 10 ] ;
r = col1 [ 11 ] ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 12 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_convert_bgr_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
b = col1 [ 0 ] ;
g = col1 [ 1 ] ;
r = col1 [ 2 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 3 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* RGBA to grayscale */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_convert_rgba_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
r = col1 [ 0 ] ;
g = col1 [ 1 ] ;
b = col1 [ 2 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 4 ] ;
g = col1 [ 5 ] ;
b = col1 [ 6 ] ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 8 ] ;
g = col1 [ 9 ] ;
b = col1 [ 10 ] ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 12 ] ;
g = col1 [ 13 ] ;
b = col1 [ 14 ] ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_convert_rgba_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
r = col1 [ 0 ] ;
g = col1 [ 1 ] ;
b = col1 [ 2 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 4 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* BGRA to grayscale */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_convert_bgra_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
b = col1 [ 0 ] ;
g = col1 [ 1 ] ;
r = col1 [ 2 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 4 ] ;
g = col1 [ 5 ] ;
r = col1 [ 6 ] ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 8 ] ;
g = col1 [ 9 ] ;
r = col1 [ 10 ] ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 12 ] ;
g = col1 [ 13 ] ;
r = col1 [ 14 ] ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_convert_bgra_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
b = col1 [ 0 ] ;
g = col1 [ 1 ] ;
r = col1 [ 2 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 4 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* ARGB to grayscale */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_convert_argb_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
r = col1 [ 1 ] ;
g = col1 [ 2 ] ;
b = col1 [ 3 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 5 ] ;
g = col1 [ 6 ] ;
b = col1 [ 7 ] ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 9 ] ;
g = col1 [ 10 ] ;
b = col1 [ 11 ] ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = col1 [ 13 ] ;
g = col1 [ 14 ] ;
b = col1 [ 15 ] ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_convert_argb_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
r = col1 [ 1 ] ;
g = col1 [ 2 ] ;
b = col1 [ 3 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 4 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* ABGR to grayscale */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_convert_abgr_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2016-04-04 22:11:48 +08:00
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
b = col1 [ 1 ] ;
g = col1 [ 2 ] ;
r = col1 [ 3 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 5 ] ;
g = col1 [ 6 ] ;
r = col1 [ 7 ] ;
result [ 1 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 9 ] ;
g = col1 [ 10 ] ;
r = col1 [ 11 ] ;
result [ 2 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = col1 [ 13 ] ;
g = col1 [ 14 ] ;
r = col1 [ 15 ] ;
result [ 3 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
col1 + = 16 ;
result + = 4 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_convert_abgr_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
2019-02-22 22:44:57 +08:00
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
b = col1 [ 1 ] ;
g = col1 [ 2 ] ;
r = col1 [ 3 ] ;
result [ 0 ] = ( r + r + b + g + g + g + g + g ) > > 3 ;
col1 + = 4 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
/* Converts a YUYV image into grayscale by extracting the Y channel */
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void fast_convert_yuyv_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
const uint16_t * yuvbuf = ( const uint16_t * ) col1 ;
const uint8_t * const max_ptr = result + count ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
while ( result < max_ptr ) {
result [ 0 ] = ( uint8_t ) yuvbuf [ 0 ] ;
result [ 1 ] = ( uint8_t ) yuvbuf [ 1 ] ;
result [ 2 ] = ( uint8_t ) yuvbuf [ 2 ] ;
result [ 3 ] = ( uint8_t ) yuvbuf [ 3 ] ;
result [ 4 ] = ( uint8_t ) yuvbuf [ 4 ] ;
result [ 5 ] = ( uint8_t ) yuvbuf [ 5 ] ;
result [ 6 ] = ( uint8_t ) yuvbuf [ 6 ] ;
result [ 7 ] = ( uint8_t ) yuvbuf [ 7 ] ;
result [ 8 ] = ( uint8_t ) yuvbuf [ 8 ] ;
result [ 9 ] = ( uint8_t ) yuvbuf [ 9 ] ;
result [ 10 ] = ( uint8_t ) yuvbuf [ 10 ] ;
result [ 11 ] = ( uint8_t ) yuvbuf [ 11 ] ;
result [ 12 ] = ( uint8_t ) yuvbuf [ 12 ] ;
result [ 13 ] = ( uint8_t ) yuvbuf [ 13 ] ;
result [ 14 ] = ( uint8_t ) yuvbuf [ 14 ] ;
result [ 15 ] = ( uint8_t ) yuvbuf [ 15 ] ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
yuvbuf + = 16 ;
result + = 16 ;
}
2013-03-17 07:45:21 +08:00
}
2019-02-22 22:44:57 +08:00
__attribute__ ( ( noinline ) ) void std_convert_yuyv_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
const uint16_t * yuvbuf = ( const uint16_t * ) col1 ;
const uint8_t * const max_ptr = result + count ;
while ( result < max_ptr ) {
result [ 0 ] = ( uint8_t ) yuvbuf [ 0 ] ;
yuvbuf + = 1 ;
result + = 1 ;
}
}
2013-03-17 07:45:21 +08:00
2017-03-22 17:38:15 +08:00
/* RGB32 to grayscale SSSE3 */
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " ssse3 " ) ) )
# endif
2017-03-22 17:38:15 +08:00
void ssse3_convert_rgb32_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count , uint32_t multiplier ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2013-03-17 07:45:21 +08:00
2017-03-22 17:38:15 +08:00
/* XMM0 - zero */
/* XMM1 - col1 */
/* XMM3 - multiplier */
/* XMM4 - divide mask */
2016-04-04 22:11:48 +08:00
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" mov $0x1F1F1F1F, %%eax \n \t "
" movd %%eax, %%xmm4 \n \t "
" pshufd $0x0, %%xmm4, %%xmm4 \n \t "
" mov %3, %%eax \n \t "
" movd %%eax, %%xmm3 \n \t "
" pshufd $0x0, %%xmm3, %%xmm3 \n \t "
" pxor %%xmm0, %%xmm0 \n \t "
" sub $0x10, %0 \n \t "
" sub $0x4, %1 \n \t "
" ssse3_convert_rgb32_gray8_iter: \n \t "
" movdqa (%0,%2,4), %%xmm1 \n \t "
" psrlq $0x3, %%xmm1 \n \t "
" pand %%xmm4, %%xmm1 \n \t "
" pmaddubsw %%xmm3, %%xmm1 \n \t "
" phaddw %%xmm0, %%xmm1 \n \t "
" packuswb %%xmm1, %%xmm1 \n \t "
" movd %%xmm1, %%eax \n \t "
" movnti %%eax, (%1,%2) \n \t "
" sub $0x4, %2 \n \t "
" jnz ssse3_convert_rgb32_gray8_iter \n \t "
:
: " r " ( col1 ) , " r " ( result ) , " r " ( count ) , " g " ( multiplier )
: " %eax " , " %xmm0 " , " %xmm1 " , " %xmm3 " , " %xmm4 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
2017-03-22 17:38:15 +08:00
/* RGBA to grayscale SSSE3 */
void ssse3_convert_rgba_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
ssse3_convert_rgb32_gray8 ( col1 , result , count , 0x00010502 ) ;
}
2017-03-15 04:29:55 +08:00
/* BGRA to grayscale SSSE3 */
void ssse3_convert_bgra_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2017-03-22 17:38:15 +08:00
ssse3_convert_rgb32_gray8 ( col1 , result , count , 0x00020501 ) ;
2017-03-15 04:29:55 +08:00
}
/* ARGB to grayscale SSSE3 */
void ssse3_convert_argb_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2017-03-22 17:38:15 +08:00
ssse3_convert_rgb32_gray8 ( col1 , result , count , 0x01050200 ) ;
2017-03-15 04:29:55 +08:00
}
/* ABGR to grayscale SSSE3 */
void ssse3_convert_abgr_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2017-03-22 17:38:15 +08:00
ssse3_convert_rgb32_gray8 ( col1 , result , count , 0x02050100 ) ;
2017-03-15 04:29:55 +08:00
}
2013-03-17 07:45:21 +08:00
/* Converts a YUYV image into grayscale by extracting the Y channel */
2014-03-16 23:51:54 +08:00
# if defined(__i386__) || defined(__x86_64__)
__attribute__ ( ( noinline , __target__ ( " ssse3 " ) ) )
# endif
void ssse3_convert_yuyv_gray8 ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
# if ((defined(__i386__) || defined(__x86_64__) || defined(ZM_KEEP_SSE)) && !defined(ZM_STRIP_SSE))
2016-04-04 22:11:48 +08:00
unsigned long i = 0 ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
__attribute__ ( ( aligned ( 16 ) ) ) static const uint8_t movemask1 [ 16 ] = { 0 , 2 , 4 , 6 , 8 , 10 , 12 , 14 , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF } ;
__attribute__ ( ( aligned ( 16 ) ) ) static const uint8_t movemask2 [ 16 ] = { 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0xFF , 0 , 2 , 4 , 6 , 8 , 10 , 12 , 14 } ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* XMM0 - General purpose */
/* XMM1 - General purpose */
/* XMM2 - unused */
/* XMM3 - shift mask 1 */
/* XMM4 - shift mask 2 */
/* XMM5 - unused*/
/* XMM6 - unused */
/* XMM7 - unused */
__asm__ __volatile__ (
2017-04-28 22:34:48 +08:00
" movdqa %4, %%xmm3 \n \t "
" movdqa %5, %%xmm4 \n \t "
" algo_ssse3_convert_yuyv_gray8: \n \t "
" movdqa (%0), %%xmm0 \n \t "
" pshufb %%xmm3, %%xmm0 \n \t "
" movdqa 0x10(%0), %%xmm1 \n \t "
" pshufb %%xmm4, %%xmm1 \n \t "
" por %%xmm1, %%xmm0 \n \t "
" movntdq %%xmm0, (%1) \n \t "
" add $0x10, %3 \n \t "
" add $0x10, %1 \n \t "
" add $0x20, %0 \n \t "
" cmp %2, %3 \n \t "
" jb algo_ssse3_convert_yuyv_gray8 \n \t "
:
2013-03-17 07:45:21 +08:00
# if (defined(_DEBUG) && !defined(__x86_64__)) /* Use one less register to allow compilation to success on 32bit with omit frame pointer disabled */
2017-04-28 22:34:48 +08:00
: " r " ( col1 ) , " r " ( result ) , " m " ( count ) , " r " ( i ) , " m " ( * movemask1 ) , " m " ( * movemask2 )
2013-03-17 07:45:21 +08:00
# else
2017-04-28 22:34:48 +08:00
: " r " ( col1 ) , " r " ( result ) , " r " ( count ) , " r " ( i ) , " m " ( * movemask1 ) , " m " ( * movemask2 )
2013-03-17 07:45:21 +08:00
# endif
2017-04-28 22:34:48 +08:00
: " %xmm3 " , " %xmm4 " , " cc " , " memory "
) ;
2013-03-17 07:45:21 +08:00
# else
2016-04-04 22:11:48 +08:00
Panic ( " SSE function called on a non x86 \\ x86-64 platform " ) ;
2013-03-17 07:45:21 +08:00
# endif
}
/* YUYV to RGB24 - relocated from zm_local_camera.cpp */
__attribute__ ( ( noinline ) ) void zm_convert_yuyv_rgb ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
unsigned int r , g , b ;
unsigned int y1 , y2 , u , v ;
for ( unsigned int i = 0 ; i < count ; i + = 2 , col1 + = 4 , result + = 6 ) {
y1 = col1 [ 0 ] ;
u = col1 [ 1 ] ;
y2 = col1 [ 2 ] ;
v = col1 [ 3 ] ;
r = y1 + r_v_table [ v ] ;
g = y1 - ( g_u_table [ u ] + g_v_table [ v ] ) ;
b = y1 + b_u_table [ u ] ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
result [ 0 ] = r < 0 ? 0 : ( r > 255 ? 255 : r ) ;
result [ 1 ] = g < 0 ? 0 : ( g > 255 ? 255 : g ) ;
result [ 2 ] = b < 0 ? 0 : ( b > 255 ? 255 : b ) ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
r = y2 + r_v_table [ v ] ;
g = y2 - ( g_u_table [ u ] + g_v_table [ v ] ) ;
b = y2 + b_u_table [ u ] ;
result [ 3 ] = r < 0 ? 0 : ( r > 255 ? 255 : r ) ;
result [ 4 ] = g < 0 ? 0 : ( g > 255 ? 255 : g ) ;
result [ 5 ] = b < 0 ? 0 : ( b > 255 ? 255 : b ) ;
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
}
/* YUYV to RGBA - modified the one above */
__attribute__ ( ( noinline ) ) void zm_convert_yuyv_rgba ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2016-04-04 22:11:48 +08:00
unsigned int r , g , b ;
unsigned int y1 , y2 , u , v ;
for ( unsigned int i = 0 ; i < count ; i + = 2 , col1 + = 4 , result + = 8 ) {
y1 = col1 [ 0 ] ;
u = col1 [ 1 ] ;
y2 = col1 [ 2 ] ;
v = col1 [ 3 ] ;
r = y1 + r_v_table [ v ] ;
g = y1 - ( g_u_table [ u ] + g_v_table [ v ] ) ;
b = y1 + b_u_table [ u ] ;
2017-04-28 22:34:48 +08:00
2017-12-13 02:30:58 +08:00
result [ 0 ] = r > 255 ? 255 : r ;
result [ 1 ] = g > 255 ? 255 : g ;
result [ 2 ] = b > 255 ? 255 : b ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
r = y2 + r_v_table [ v ] ;
g = y2 - ( g_u_table [ u ] + g_v_table [ v ] ) ;
b = y2 + b_u_table [ u ] ;
2017-12-13 02:30:58 +08:00
result [ 4 ] = r > 255 ? 255 : r ;
result [ 5 ] = g > 255 ? 255 : g ;
result [ 6 ] = b > 255 ? 255 : b ;
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2013-03-17 07:45:21 +08:00
}
/* RGB555 to RGB24 - relocated from zm_local_camera.cpp */
__attribute__ ( ( noinline ) ) void zm_convert_rgb555_rgb ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
for ( unsigned int i = 0 ; i < count ; i + + , col1 + = 2 , result + = 3 ) {
2016-04-04 22:11:48 +08:00
b = ( ( * col1 ) < < 3 ) & 0xf8 ;
g = ( ( ( * ( col1 + 1 ) ) < < 6 ) | ( ( * col1 ) > > 2 ) ) & 0xf8 ;
r = ( ( * ( col1 + 1 ) ) < < 1 ) & 0xf8 ;
result [ 0 ] = r ;
result [ 1 ] = g ;
result [ 2 ] = b ;
}
2013-03-17 07:45:21 +08:00
}
/* RGB555 to RGBA - modified the one above */
__attribute__ ( ( noinline ) ) void zm_convert_rgb555_rgba ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
for ( unsigned int i = 0 ; i < count ; i + + , col1 + = 2 , result + = 4 ) {
2016-04-04 22:11:48 +08:00
b = ( ( * col1 ) < < 3 ) & 0xf8 ;
g = ( ( ( * ( col1 + 1 ) ) < < 6 ) | ( ( * col1 ) > > 2 ) ) & 0xf8 ;
r = ( ( * ( col1 + 1 ) ) < < 1 ) & 0xf8 ;
result [ 0 ] = r ;
result [ 1 ] = g ;
result [ 2 ] = b ;
}
2013-03-17 07:45:21 +08:00
}
/* RGB565 to RGB24 - relocated from zm_local_camera.cpp */
__attribute__ ( ( noinline ) ) void zm_convert_rgb565_rgb ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
for ( unsigned int i = 0 ; i < count ; i + + , col1 + = 2 , result + = 3 ) {
2016-04-04 22:11:48 +08:00
b = ( ( * col1 ) < < 3 ) & 0xf8 ;
g = ( ( ( * ( col1 + 1 ) ) < < 5 ) | ( ( * col1 ) > > 3 ) ) & 0xfc ;
r = ( * ( col1 + 1 ) ) & 0xf8 ;
result [ 0 ] = r ;
result [ 1 ] = g ;
result [ 2 ] = b ;
}
2013-03-17 07:45:21 +08:00
}
/* RGB565 to RGBA - modified the one above */
__attribute__ ( ( noinline ) ) void zm_convert_rgb565_rgba ( const uint8_t * col1 , uint8_t * result , unsigned long count ) {
2019-09-17 22:31:48 +08:00
unsigned int r , g , b ;
for ( unsigned int i = 0 ; i < count ; i + + , col1 + = 2 , result + = 4 ) {
2016-04-04 22:11:48 +08:00
b = ( ( * col1 ) < < 3 ) & 0xf8 ;
g = ( ( ( * ( col1 + 1 ) ) < < 5 ) | ( ( * col1 ) > > 3 ) ) & 0xfc ;
r = ( * ( col1 + 1 ) ) & 0xf8 ;
result [ 0 ] = r ;
result [ 1 ] = g ;
result [ 2 ] = b ;
}
2013-03-17 07:45:21 +08:00
}
/************************************************* DEINTERLACE FUNCTIONS *************************************************/
/* Grayscale */
2018-10-22 22:41:27 +08:00
__attribute__ ( ( noinline ) ) void std_deinterlace_4field_gray8 ( uint8_t * col1 , uint8_t * col2 , unsigned int threshold , unsigned int width , unsigned int height ) {
2016-04-04 22:11:48 +08:00
uint8_t * pcurrent , * pabove , * pncurrent , * pnabove , * pbelow ;
const uint8_t * const max_ptr = col1 + ( width * ( height - 1 ) ) ;
const uint8_t * max_ptr2 ;
pcurrent = col1 + width ;
pncurrent = col2 + width ;
pabove = col1 ;
pnabove = col2 ;
pbelow = col1 + ( width * 2 ) ;
2018-10-22 22:41:27 +08:00
while ( pcurrent < max_ptr ) {
2016-04-04 22:11:48 +08:00
max_ptr2 = pcurrent + width ;
while ( pcurrent < max_ptr2 ) {
if ( ( unsigned int ) ( ( abs ( * pnabove - * pabove ) + abs ( * pncurrent - * pcurrent ) ) > > 1 ) > = threshold ) {
* pcurrent = ( * pabove + * pbelow ) > > 1 ;
}
pabove + + ;
pnabove + + ;
pcurrent + + ;
pncurrent + + ;
pbelow + + ;
}
pcurrent + = width ;
pncurrent + = width ;
pabove + = width ;
pnabove + = width ;
pbelow + = width ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Special case for the last line */
max_ptr2 = pcurrent + width ;
while ( pcurrent < max_ptr2 ) {
if ( ( unsigned int ) ( ( abs ( * pnabove - * pabove ) + abs ( * pncurrent - * pcurrent ) ) > > 1 ) > = threshold ) {
* pcurrent = * pabove ;
}
pabove + + ;
pnabove + + ;
pcurrent + + ;
pncurrent + + ;
}
2013-03-17 07:45:21 +08:00
}
/* RGB */
2018-10-22 22:41:27 +08:00
__attribute__ ( ( noinline ) ) void std_deinterlace_4field_rgb ( uint8_t * col1 , uint8_t * col2 , unsigned int threshold , unsigned int width , unsigned int height ) {
2016-04-04 22:11:48 +08:00
uint8_t * pcurrent , * pabove , * pncurrent , * pnabove , * pbelow ;
const unsigned int row_width = width * 3 ;
const uint8_t * const max_ptr = col1 + ( row_width * ( height - 1 ) ) ;
const uint8_t * max_ptr2 ;
unsigned int b , g , r ;
unsigned int delta1 , delta2 ;
pcurrent = col1 + ( width * 3 ) ;
pncurrent = col2 + ( width * 3 ) ;
pabove = col1 ;
pnabove = col2 ;
pbelow = col1 + ( ( width * 2 ) * 3 ) ;
2018-10-22 22:41:27 +08:00
while ( pcurrent < max_ptr ) {
2016-04-04 22:11:48 +08:00
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
r = abs ( pnabove [ 0 ] - pabove [ 0 ] ) ;
g = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
b = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( pncurrent [ 0 ] - pcurrent [ 0 ] ) ;
g = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
b = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 0 ] = ( pabove [ 0 ] + pbelow [ 0 ] ) > > 1 ;
pcurrent [ 1 ] = ( pabove [ 1 ] + pbelow [ 1 ] ) > > 1 ;
pcurrent [ 2 ] = ( pabove [ 2 ] + pbelow [ 2 ] ) > > 1 ;
}
pabove + = 3 ;
pnabove + = 3 ;
pcurrent + = 3 ;
pncurrent + = 3 ;
pbelow + = 3 ;
}
pcurrent + = row_width ;
pncurrent + = row_width ;
pabove + = row_width ;
pnabove + = row_width ;
pbelow + = row_width ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Special case for the last line */
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
2017-04-28 22:34:48 +08:00
r = abs ( pnabove [ 0 ] - pabove [ 0 ] ) ;
g = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
b = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( pncurrent [ 0 ] - pcurrent [ 0 ] ) ;
g = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
b = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 0 ] = pabove [ 0 ] ;
pcurrent [ 1 ] = pabove [ 1 ] ;
pcurrent [ 2 ] = pabove [ 2 ] ;
}
pabove + = 3 ;
pnabove + = 3 ;
pcurrent + = 3 ;
pncurrent + = 3 ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/* BGR */
2018-10-22 22:41:27 +08:00
__attribute__ ( ( noinline ) ) void std_deinterlace_4field_bgr ( uint8_t * col1 , uint8_t * col2 , unsigned int threshold , unsigned int width , unsigned int height ) {
2016-04-04 22:11:48 +08:00
uint8_t * pcurrent , * pabove , * pncurrent , * pnabove , * pbelow ;
const unsigned int row_width = width * 3 ;
const uint8_t * const max_ptr = col1 + ( row_width * ( height - 1 ) ) ;
const uint8_t * max_ptr2 ;
unsigned int b , g , r ;
unsigned int delta1 , delta2 ;
pcurrent = col1 + ( width * 3 ) ;
pncurrent = col2 + ( width * 3 ) ;
pabove = col1 ;
pnabove = col2 ;
pbelow = col1 + ( ( width * 2 ) * 3 ) ;
2018-10-22 22:41:27 +08:00
while ( pcurrent < max_ptr ) {
2016-04-04 22:11:48 +08:00
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
b = abs ( pnabove [ 0 ] - pabove [ 0 ] ) ;
g = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
r = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( pncurrent [ 0 ] - pcurrent [ 0 ] ) ;
g = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
r = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 0 ] = ( pabove [ 0 ] + pbelow [ 0 ] ) > > 1 ;
pcurrent [ 1 ] = ( pabove [ 1 ] + pbelow [ 1 ] ) > > 1 ;
pcurrent [ 2 ] = ( pabove [ 2 ] + pbelow [ 2 ] ) > > 1 ;
}
pabove + = 3 ;
pnabove + = 3 ;
pcurrent + = 3 ;
pncurrent + = 3 ;
pbelow + = 3 ;
}
pcurrent + = row_width ;
pncurrent + = row_width ;
pabove + = row_width ;
pnabove + = row_width ;
pbelow + = row_width ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Special case for the last line */
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
2017-04-28 22:34:48 +08:00
b = abs ( pnabove [ 0 ] - pabove [ 0 ] ) ;
g = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
r = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( pncurrent [ 0 ] - pcurrent [ 0 ] ) ;
g = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
r = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 0 ] = pabove [ 0 ] ;
pcurrent [ 1 ] = pabove [ 1 ] ;
pcurrent [ 2 ] = pabove [ 2 ] ;
}
pabove + = 3 ;
pnabove + = 3 ;
pcurrent + = 3 ;
pncurrent + = 3 ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/* RGBA */
2018-10-22 22:41:27 +08:00
__attribute__ ( ( noinline ) ) void std_deinterlace_4field_rgba ( uint8_t * col1 , uint8_t * col2 , unsigned int threshold , unsigned int width , unsigned int height ) {
2016-04-04 22:11:48 +08:00
uint8_t * pcurrent , * pabove , * pncurrent , * pnabove , * pbelow ;
const unsigned int row_width = width * 4 ;
const uint8_t * const max_ptr = col1 + ( row_width * ( height - 1 ) ) ;
const uint8_t * max_ptr2 ;
unsigned int b , g , r ;
unsigned int delta1 , delta2 ;
pcurrent = col1 + row_width ;
pncurrent = col2 + row_width ;
pabove = col1 ;
pnabove = col2 ;
pbelow = col1 + ( row_width * 2 ) ;
2018-10-22 22:41:27 +08:00
while ( pcurrent < max_ptr ) {
2016-04-04 22:11:48 +08:00
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
r = abs ( pnabove [ 0 ] - pabove [ 0 ] ) ;
g = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
b = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( pncurrent [ 0 ] - pcurrent [ 0 ] ) ;
g = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
b = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 0 ] = ( pabove [ 0 ] + pbelow [ 0 ] ) > > 1 ;
pcurrent [ 1 ] = ( pabove [ 1 ] + pbelow [ 1 ] ) > > 1 ;
pcurrent [ 2 ] = ( pabove [ 2 ] + pbelow [ 2 ] ) > > 1 ;
}
pabove + = 4 ;
pnabove + = 4 ;
pcurrent + = 4 ;
pncurrent + = 4 ;
pbelow + = 4 ;
}
pcurrent + = row_width ;
pncurrent + = row_width ;
pabove + = row_width ;
pnabove + = row_width ;
pbelow + = row_width ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Special case for the last line */
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
2017-04-28 22:34:48 +08:00
r = abs ( pnabove [ 0 ] - pabove [ 0 ] ) ;
g = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
b = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( pncurrent [ 0 ] - pcurrent [ 0 ] ) ;
g = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
b = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 0 ] = pabove [ 0 ] ;
pcurrent [ 1 ] = pabove [ 1 ] ;
pcurrent [ 2 ] = pabove [ 2 ] ;
}
pabove + = 4 ;
pnabove + = 4 ;
pcurrent + = 4 ;
pncurrent + = 4 ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/* BGRA */
2018-10-22 22:41:27 +08:00
__attribute__ ( ( noinline ) ) void std_deinterlace_4field_bgra ( uint8_t * col1 , uint8_t * col2 , unsigned int threshold , unsigned int width , unsigned int height ) {
2016-04-04 22:11:48 +08:00
uint8_t * pcurrent , * pabove , * pncurrent , * pnabove , * pbelow ;
const unsigned int row_width = width * 4 ;
const uint8_t * const max_ptr = col1 + ( row_width * ( height - 1 ) ) ;
const uint8_t * max_ptr2 ;
unsigned int b , g , r ;
unsigned int delta1 , delta2 ;
pcurrent = col1 + row_width ;
pncurrent = col2 + row_width ;
pabove = col1 ;
pnabove = col2 ;
pbelow = col1 + ( row_width * 2 ) ;
2018-10-22 22:41:27 +08:00
while ( pcurrent < max_ptr ) {
2016-04-04 22:11:48 +08:00
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
b = abs ( pnabove [ 0 ] - pabove [ 0 ] ) ;
g = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
r = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( pncurrent [ 0 ] - pcurrent [ 0 ] ) ;
g = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
r = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 0 ] = ( pabove [ 0 ] + pbelow [ 0 ] ) > > 1 ;
pcurrent [ 1 ] = ( pabove [ 1 ] + pbelow [ 1 ] ) > > 1 ;
pcurrent [ 2 ] = ( pabove [ 2 ] + pbelow [ 2 ] ) > > 1 ;
}
pabove + = 4 ;
pnabove + = 4 ;
pcurrent + = 4 ;
pncurrent + = 4 ;
pbelow + = 4 ;
}
pcurrent + = row_width ;
pncurrent + = row_width ;
pabove + = row_width ;
pnabove + = row_width ;
pbelow + = row_width ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Special case for the last line */
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
2017-04-28 22:34:48 +08:00
b = abs ( pnabove [ 0 ] - pabove [ 0 ] ) ;
g = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
r = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( pncurrent [ 0 ] - pcurrent [ 0 ] ) ;
g = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
r = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 0 ] = pabove [ 0 ] ;
pcurrent [ 1 ] = pabove [ 1 ] ;
pcurrent [ 2 ] = pabove [ 2 ] ;
}
pabove + = 4 ;
pnabove + = 4 ;
pcurrent + = 4 ;
pncurrent + = 4 ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/* ARGB */
2018-10-22 22:41:27 +08:00
__attribute__ ( ( noinline ) ) void std_deinterlace_4field_argb ( uint8_t * col1 , uint8_t * col2 , unsigned int threshold , unsigned int width , unsigned int height ) {
2016-04-04 22:11:48 +08:00
uint8_t * pcurrent , * pabove , * pncurrent , * pnabove , * pbelow ;
const unsigned int row_width = width * 4 ;
const uint8_t * const max_ptr = col1 + ( row_width * ( height - 1 ) ) ;
const uint8_t * max_ptr2 ;
unsigned int b , g , r ;
unsigned int delta1 , delta2 ;
pcurrent = col1 + row_width ;
pncurrent = col2 + row_width ;
pabove = col1 ;
pnabove = col2 ;
pbelow = col1 + ( row_width * 2 ) ;
2018-10-22 22:41:27 +08:00
while ( pcurrent < max_ptr ) {
2016-04-04 22:11:48 +08:00
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
r = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
g = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
b = abs ( pnabove [ 3 ] - pabove [ 3 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
g = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
b = abs ( pncurrent [ 3 ] - pcurrent [ 3 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 1 ] = ( pabove [ 1 ] + pbelow [ 1 ] ) > > 1 ;
pcurrent [ 2 ] = ( pabove [ 2 ] + pbelow [ 2 ] ) > > 1 ;
pcurrent [ 3 ] = ( pabove [ 3 ] + pbelow [ 3 ] ) > > 1 ;
}
pabove + = 4 ;
pnabove + = 4 ;
pcurrent + = 4 ;
pncurrent + = 4 ;
pbelow + = 4 ;
}
pcurrent + = row_width ;
pncurrent + = row_width ;
pabove + = row_width ;
pnabove + = row_width ;
pbelow + = row_width ;
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Special case for the last line */
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
2017-04-28 22:34:48 +08:00
r = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
g = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
b = abs ( pnabove [ 3 ] - pabove [ 3 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
r = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
g = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
b = abs ( pncurrent [ 3 ] - pcurrent [ 3 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 1 ] = pabove [ 1 ] ;
pcurrent [ 2 ] = pabove [ 2 ] ;
pcurrent [ 3 ] = pabove [ 3 ] ;
}
pabove + = 4 ;
pnabove + = 4 ;
pcurrent + = 4 ;
pncurrent + = 4 ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
/* ABGR */
2018-10-22 22:41:27 +08:00
__attribute__ ( ( noinline ) ) void std_deinterlace_4field_abgr ( uint8_t * col1 , uint8_t * col2 , unsigned int threshold , unsigned int width , unsigned int height ) {
2016-04-04 22:11:48 +08:00
uint8_t * pcurrent , * pabove , * pncurrent , * pnabove , * pbelow ;
const unsigned int row_width = width * 4 ;
const uint8_t * const max_ptr = col1 + ( row_width * ( height - 1 ) ) ;
const uint8_t * max_ptr2 ;
unsigned int b , g , r ;
unsigned int delta1 , delta2 ;
pcurrent = col1 + row_width ;
pncurrent = col2 + row_width ;
pabove = col1 ;
pnabove = col2 ;
pbelow = col1 + ( row_width * 2 ) ;
2018-10-22 22:41:27 +08:00
while ( pcurrent < max_ptr ) {
2016-04-04 22:11:48 +08:00
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
b = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
g = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
r = abs ( pnabove [ 3 ] - pabove [ 3 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
g = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
r = abs ( pncurrent [ 3 ] - pcurrent [ 3 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 1 ] = ( pabove [ 1 ] + pbelow [ 1 ] ) > > 1 ;
pcurrent [ 2 ] = ( pabove [ 2 ] + pbelow [ 2 ] ) > > 1 ;
pcurrent [ 3 ] = ( pabove [ 3 ] + pbelow [ 3 ] ) > > 1 ;
}
pabove + = 4 ;
pnabove + = 4 ;
pcurrent + = 4 ;
pncurrent + = 4 ;
pbelow + = 4 ;
}
pcurrent + = row_width ;
pncurrent + = row_width ;
pabove + = row_width ;
pnabove + = row_width ;
pbelow + = row_width ;
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
}
2017-04-28 22:34:48 +08:00
2016-04-04 22:11:48 +08:00
/* Special case for the last line */
max_ptr2 = pcurrent + row_width ;
while ( pcurrent < max_ptr2 ) {
2017-04-28 22:34:48 +08:00
b = abs ( pnabove [ 1 ] - pabove [ 1 ] ) ;
g = abs ( pnabove [ 2 ] - pabove [ 2 ] ) ;
r = abs ( pnabove [ 3 ] - pabove [ 3 ] ) ;
delta1 = ( r + r + b + g + g + g + g + g ) > > 3 ;
b = abs ( pncurrent [ 1 ] - pcurrent [ 1 ] ) ;
g = abs ( pncurrent [ 2 ] - pcurrent [ 2 ] ) ;
r = abs ( pncurrent [ 3 ] - pcurrent [ 3 ] ) ;
delta2 = ( r + r + b + g + g + g + g + g ) > > 3 ;
if ( ( ( delta1 + delta2 ) > > 1 ) > = threshold ) {
pcurrent [ 1 ] = pabove [ 1 ] ;
pcurrent [ 2 ] = pabove [ 2 ] ;
pcurrent [ 3 ] = pabove [ 3 ] ;
}
pabove + = 4 ;
pnabove + = 4 ;
pcurrent + = 4 ;
pncurrent + = 4 ;
2016-04-04 22:11:48 +08:00
}
2013-03-17 07:45:21 +08:00
}
2021-01-12 00:57:34 +08:00