1cabdff1aSopenharmony_ci/* 2cabdff1aSopenharmony_ci * QuickTime RPZA Video Encoder 3cabdff1aSopenharmony_ci * 4cabdff1aSopenharmony_ci * This file is part of FFmpeg. 5cabdff1aSopenharmony_ci * 6cabdff1aSopenharmony_ci * FFmpeg is free software; you can redistribute it and/or 7cabdff1aSopenharmony_ci * modify it under the terms of the GNU Lesser General Public 8cabdff1aSopenharmony_ci * License as published by the Free Software Foundation; either 9cabdff1aSopenharmony_ci * version 2.1 of the License, or (at your option) any later version. 10cabdff1aSopenharmony_ci * 11cabdff1aSopenharmony_ci * FFmpeg is distributed in the hope that it will be useful, 12cabdff1aSopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 13cabdff1aSopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14cabdff1aSopenharmony_ci * Lesser General Public License for more details. 15cabdff1aSopenharmony_ci * 16cabdff1aSopenharmony_ci * You should have received a copy of the GNU Lesser General Public 17cabdff1aSopenharmony_ci * License along with FFmpeg; if not, write to the Free Software 18cabdff1aSopenharmony_ci * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19cabdff1aSopenharmony_ci */ 20cabdff1aSopenharmony_ci 21cabdff1aSopenharmony_ci/** 22cabdff1aSopenharmony_ci * @file rpzaenc.c 23cabdff1aSopenharmony_ci * QT RPZA Video Encoder by Todd Kirby <doubleshot@pacbell.net> and David Adler 24cabdff1aSopenharmony_ci */ 25cabdff1aSopenharmony_ci 26cabdff1aSopenharmony_ci#include "libavutil/avassert.h" 27cabdff1aSopenharmony_ci#include "libavutil/common.h" 28cabdff1aSopenharmony_ci#include "libavutil/opt.h" 29cabdff1aSopenharmony_ci 30cabdff1aSopenharmony_ci#include "avcodec.h" 31cabdff1aSopenharmony_ci#include "codec_internal.h" 32cabdff1aSopenharmony_ci#include "encode.h" 33cabdff1aSopenharmony_ci#include "put_bits.h" 34cabdff1aSopenharmony_ci 35cabdff1aSopenharmony_citypedef struct RpzaContext { 36cabdff1aSopenharmony_ci AVClass *avclass; 37cabdff1aSopenharmony_ci 38cabdff1aSopenharmony_ci int skip_frame_thresh; 39cabdff1aSopenharmony_ci int start_one_color_thresh; 40cabdff1aSopenharmony_ci int continue_one_color_thresh; 41cabdff1aSopenharmony_ci int sixteen_color_thresh; 42cabdff1aSopenharmony_ci 43cabdff1aSopenharmony_ci AVFrame *prev_frame; // buffer for previous source frame 44cabdff1aSopenharmony_ci PutBitContext pb; // buffer for encoded frame data. 45cabdff1aSopenharmony_ci 46cabdff1aSopenharmony_ci int frame_width; // width in pixels of source frame 47cabdff1aSopenharmony_ci int frame_height; // height in pixesl of source frame 48cabdff1aSopenharmony_ci 49cabdff1aSopenharmony_ci int first_frame; // flag set to one when the first frame is being processed 50cabdff1aSopenharmony_ci // so that comparisons with previous frame data in not attempted 51cabdff1aSopenharmony_ci} RpzaContext; 52cabdff1aSopenharmony_ci 53cabdff1aSopenharmony_citypedef enum channel_offset { 54cabdff1aSopenharmony_ci RED = 2, 55cabdff1aSopenharmony_ci GREEN = 1, 56cabdff1aSopenharmony_ci BLUE = 0, 57cabdff1aSopenharmony_ci} channel_offset; 58cabdff1aSopenharmony_ci 59cabdff1aSopenharmony_citypedef struct rgb { 60cabdff1aSopenharmony_ci uint8_t r; 61cabdff1aSopenharmony_ci uint8_t g; 62cabdff1aSopenharmony_ci uint8_t b; 63cabdff1aSopenharmony_ci} rgb; 64cabdff1aSopenharmony_ci 65cabdff1aSopenharmony_ci#define SQR(x) ((x) * (x)) 66cabdff1aSopenharmony_ci 67cabdff1aSopenharmony_ci/* 15 bit components */ 68cabdff1aSopenharmony_ci#define GET_CHAN(color, chan) (((color) >> ((chan) * 5) & 0x1F) * 8) 69cabdff1aSopenharmony_ci#define R(color) GET_CHAN(color, RED) 70cabdff1aSopenharmony_ci#define G(color) GET_CHAN(color, GREEN) 71cabdff1aSopenharmony_ci#define B(color) GET_CHAN(color, BLUE) 72cabdff1aSopenharmony_ci 73cabdff1aSopenharmony_citypedef struct BlockInfo { 74cabdff1aSopenharmony_ci int row; 75cabdff1aSopenharmony_ci int col; 76cabdff1aSopenharmony_ci int block_width; 77cabdff1aSopenharmony_ci int block_height; 78cabdff1aSopenharmony_ci int image_width; 79cabdff1aSopenharmony_ci int image_height; 80cabdff1aSopenharmony_ci int block_index; 81cabdff1aSopenharmony_ci uint16_t start; 82cabdff1aSopenharmony_ci int rowstride; 83cabdff1aSopenharmony_ci int blocks_per_row; 84cabdff1aSopenharmony_ci int total_blocks; 85cabdff1aSopenharmony_ci} BlockInfo; 86cabdff1aSopenharmony_ci 87cabdff1aSopenharmony_cistatic void get_colors(uint8_t *min, uint8_t *max, uint8_t color4[4][3]) 88cabdff1aSopenharmony_ci{ 89cabdff1aSopenharmony_ci uint8_t step; 90cabdff1aSopenharmony_ci 91cabdff1aSopenharmony_ci color4[0][0] = min[0]; 92cabdff1aSopenharmony_ci color4[0][1] = min[1]; 93cabdff1aSopenharmony_ci color4[0][2] = min[2]; 94cabdff1aSopenharmony_ci 95cabdff1aSopenharmony_ci color4[3][0] = max[0]; 96cabdff1aSopenharmony_ci color4[3][1] = max[1]; 97cabdff1aSopenharmony_ci color4[3][2] = max[2]; 98cabdff1aSopenharmony_ci 99cabdff1aSopenharmony_ci // red components 100cabdff1aSopenharmony_ci step = (color4[3][0] - color4[0][0] + 1) / 3; 101cabdff1aSopenharmony_ci color4[1][0] = color4[0][0] + step; 102cabdff1aSopenharmony_ci color4[2][0] = color4[3][0] - step; 103cabdff1aSopenharmony_ci 104cabdff1aSopenharmony_ci // green components 105cabdff1aSopenharmony_ci step = (color4[3][1] - color4[0][1] + 1) / 3; 106cabdff1aSopenharmony_ci color4[1][1] = color4[0][1] + step; 107cabdff1aSopenharmony_ci color4[2][1] = color4[3][1] - step; 108cabdff1aSopenharmony_ci 109cabdff1aSopenharmony_ci // blue components 110cabdff1aSopenharmony_ci step = (color4[3][2] - color4[0][2] + 1) / 3; 111cabdff1aSopenharmony_ci color4[1][2] = color4[0][2] + step; 112cabdff1aSopenharmony_ci color4[2][2] = color4[3][2] - step; 113cabdff1aSopenharmony_ci} 114cabdff1aSopenharmony_ci 115cabdff1aSopenharmony_ci/* Fill BlockInfo struct with information about a 4x4 block of the image */ 116cabdff1aSopenharmony_cistatic int get_block_info(BlockInfo *bi, int block) 117cabdff1aSopenharmony_ci{ 118cabdff1aSopenharmony_ci bi->row = block / bi->blocks_per_row; 119cabdff1aSopenharmony_ci bi->col = block % bi->blocks_per_row; 120cabdff1aSopenharmony_ci 121cabdff1aSopenharmony_ci // test for right edge block 122cabdff1aSopenharmony_ci if (bi->col == bi->blocks_per_row - 1 && (bi->image_width % 4) != 0) { 123cabdff1aSopenharmony_ci bi->block_width = bi->image_width % 4; 124cabdff1aSopenharmony_ci } else { 125cabdff1aSopenharmony_ci bi->block_width = 4; 126cabdff1aSopenharmony_ci } 127cabdff1aSopenharmony_ci 128cabdff1aSopenharmony_ci // test for bottom edge block 129cabdff1aSopenharmony_ci if (bi->row == (bi->image_height / 4) && (bi->image_height % 4) != 0) { 130cabdff1aSopenharmony_ci bi->block_height = bi->image_height % 4; 131cabdff1aSopenharmony_ci } else { 132cabdff1aSopenharmony_ci bi->block_height = 4; 133cabdff1aSopenharmony_ci } 134cabdff1aSopenharmony_ci 135cabdff1aSopenharmony_ci return block ? (bi->col * 4) + (bi->row * bi->rowstride * 4) : 0; 136cabdff1aSopenharmony_ci} 137cabdff1aSopenharmony_ci 138cabdff1aSopenharmony_cistatic uint16_t rgb24_to_rgb555(uint8_t *rgb24) 139cabdff1aSopenharmony_ci{ 140cabdff1aSopenharmony_ci uint16_t rgb555 = 0; 141cabdff1aSopenharmony_ci uint32_t r, g, b; 142cabdff1aSopenharmony_ci 143cabdff1aSopenharmony_ci r = rgb24[0] >> 3; 144cabdff1aSopenharmony_ci g = rgb24[1] >> 3; 145cabdff1aSopenharmony_ci b = rgb24[2] >> 3; 146cabdff1aSopenharmony_ci 147cabdff1aSopenharmony_ci rgb555 |= (r << 10); 148cabdff1aSopenharmony_ci rgb555 |= (g << 5); 149cabdff1aSopenharmony_ci rgb555 |= (b << 0); 150cabdff1aSopenharmony_ci 151cabdff1aSopenharmony_ci return rgb555; 152cabdff1aSopenharmony_ci} 153cabdff1aSopenharmony_ci 154cabdff1aSopenharmony_ci/* 155cabdff1aSopenharmony_ci * Returns the total difference between two 24 bit color values 156cabdff1aSopenharmony_ci */ 157cabdff1aSopenharmony_cistatic int diff_colors(uint8_t *colorA, uint8_t *colorB) 158cabdff1aSopenharmony_ci{ 159cabdff1aSopenharmony_ci int tot; 160cabdff1aSopenharmony_ci 161cabdff1aSopenharmony_ci tot = SQR(colorA[0] - colorB[0]); 162cabdff1aSopenharmony_ci tot += SQR(colorA[1] - colorB[1]); 163cabdff1aSopenharmony_ci tot += SQR(colorA[2] - colorB[2]); 164cabdff1aSopenharmony_ci 165cabdff1aSopenharmony_ci return tot; 166cabdff1aSopenharmony_ci} 167cabdff1aSopenharmony_ci 168cabdff1aSopenharmony_ci/* 169cabdff1aSopenharmony_ci * Returns the maximum channel difference 170cabdff1aSopenharmony_ci */ 171cabdff1aSopenharmony_cistatic int max_component_diff(uint16_t *colorA, uint16_t *colorB) 172cabdff1aSopenharmony_ci{ 173cabdff1aSopenharmony_ci int diff, max = 0; 174cabdff1aSopenharmony_ci 175cabdff1aSopenharmony_ci diff = FFABS(R(colorA[0]) - R(colorB[0])); 176cabdff1aSopenharmony_ci if (diff > max) { 177cabdff1aSopenharmony_ci max = diff; 178cabdff1aSopenharmony_ci } 179cabdff1aSopenharmony_ci diff = FFABS(G(colorA[0]) - G(colorB[0])); 180cabdff1aSopenharmony_ci if (diff > max) { 181cabdff1aSopenharmony_ci max = diff; 182cabdff1aSopenharmony_ci } 183cabdff1aSopenharmony_ci diff = FFABS(B(colorA[0]) - B(colorB[0])); 184cabdff1aSopenharmony_ci if (diff > max) { 185cabdff1aSopenharmony_ci max = diff; 186cabdff1aSopenharmony_ci } 187cabdff1aSopenharmony_ci return max * 8; 188cabdff1aSopenharmony_ci} 189cabdff1aSopenharmony_ci 190cabdff1aSopenharmony_ci/* 191cabdff1aSopenharmony_ci * Find the channel that has the largest difference between minimum and maximum 192cabdff1aSopenharmony_ci * color values. Put the minimum value in min, maximum in max and the channel 193cabdff1aSopenharmony_ci * in chan. 194cabdff1aSopenharmony_ci */ 195cabdff1aSopenharmony_cistatic void get_max_component_diff(BlockInfo *bi, uint16_t *block_ptr, 196cabdff1aSopenharmony_ci uint8_t *min, uint8_t *max, channel_offset *chan) 197cabdff1aSopenharmony_ci{ 198cabdff1aSopenharmony_ci int x, y; 199cabdff1aSopenharmony_ci uint8_t min_r, max_r, min_g, max_g, min_b, max_b; 200cabdff1aSopenharmony_ci uint8_t r, g, b; 201cabdff1aSopenharmony_ci 202cabdff1aSopenharmony_ci // fix warning about uninitialized vars 203cabdff1aSopenharmony_ci min_r = min_g = min_b = UINT8_MAX; 204cabdff1aSopenharmony_ci max_r = max_g = max_b = 0; 205cabdff1aSopenharmony_ci 206cabdff1aSopenharmony_ci // loop thru and compare pixels 207cabdff1aSopenharmony_ci for (y = 0; y < bi->block_height; y++) { 208cabdff1aSopenharmony_ci for (x = 0; x < bi->block_width; x++) { 209cabdff1aSopenharmony_ci // TODO: optimize 210cabdff1aSopenharmony_ci min_r = FFMIN(R(block_ptr[x]), min_r); 211cabdff1aSopenharmony_ci min_g = FFMIN(G(block_ptr[x]), min_g); 212cabdff1aSopenharmony_ci min_b = FFMIN(B(block_ptr[x]), min_b); 213cabdff1aSopenharmony_ci 214cabdff1aSopenharmony_ci max_r = FFMAX(R(block_ptr[x]), max_r); 215cabdff1aSopenharmony_ci max_g = FFMAX(G(block_ptr[x]), max_g); 216cabdff1aSopenharmony_ci max_b = FFMAX(B(block_ptr[x]), max_b); 217cabdff1aSopenharmony_ci } 218cabdff1aSopenharmony_ci block_ptr += bi->rowstride; 219cabdff1aSopenharmony_ci } 220cabdff1aSopenharmony_ci 221cabdff1aSopenharmony_ci r = max_r - min_r; 222cabdff1aSopenharmony_ci g = max_g - min_g; 223cabdff1aSopenharmony_ci b = max_b - min_b; 224cabdff1aSopenharmony_ci 225cabdff1aSopenharmony_ci if (r > g && r > b) { 226cabdff1aSopenharmony_ci *max = max_r; 227cabdff1aSopenharmony_ci *min = min_r; 228cabdff1aSopenharmony_ci *chan = RED; 229cabdff1aSopenharmony_ci } else if (g > b && g >= r) { 230cabdff1aSopenharmony_ci *max = max_g; 231cabdff1aSopenharmony_ci *min = min_g; 232cabdff1aSopenharmony_ci *chan = GREEN; 233cabdff1aSopenharmony_ci } else { 234cabdff1aSopenharmony_ci *max = max_b; 235cabdff1aSopenharmony_ci *min = min_b; 236cabdff1aSopenharmony_ci *chan = BLUE; 237cabdff1aSopenharmony_ci } 238cabdff1aSopenharmony_ci} 239cabdff1aSopenharmony_ci 240cabdff1aSopenharmony_ci/* 241cabdff1aSopenharmony_ci * Compare two 4x4 blocks to determine if the total difference between the 242cabdff1aSopenharmony_ci * blocks is greater than the thresh parameter. Returns -1 if difference 243cabdff1aSopenharmony_ci * exceeds threshold or zero otherwise. 244cabdff1aSopenharmony_ci */ 245cabdff1aSopenharmony_cistatic int compare_blocks(uint16_t *block1, uint16_t *block2, BlockInfo *bi, int thresh) 246cabdff1aSopenharmony_ci{ 247cabdff1aSopenharmony_ci int x, y, diff = 0; 248cabdff1aSopenharmony_ci for (y = 0; y < bi->block_height; y++) { 249cabdff1aSopenharmony_ci for (x = 0; x < bi->block_width; x++) { 250cabdff1aSopenharmony_ci diff = max_component_diff(&block1[x], &block2[x]); 251cabdff1aSopenharmony_ci if (diff >= thresh) { 252cabdff1aSopenharmony_ci return -1; 253cabdff1aSopenharmony_ci } 254cabdff1aSopenharmony_ci } 255cabdff1aSopenharmony_ci block1 += bi->rowstride; 256cabdff1aSopenharmony_ci block2 += bi->rowstride; 257cabdff1aSopenharmony_ci } 258cabdff1aSopenharmony_ci return 0; 259cabdff1aSopenharmony_ci} 260cabdff1aSopenharmony_ci 261cabdff1aSopenharmony_ci/* 262cabdff1aSopenharmony_ci * Determine the fit of one channel to another within a 4x4 block. This 263cabdff1aSopenharmony_ci * is used to determine the best palette choices for 4-color encoding. 264cabdff1aSopenharmony_ci */ 265cabdff1aSopenharmony_cistatic int leastsquares(uint16_t *block_ptr, BlockInfo *bi, 266cabdff1aSopenharmony_ci channel_offset xchannel, channel_offset ychannel, 267cabdff1aSopenharmony_ci double *slope, double *y_intercept, double *correlation_coef) 268cabdff1aSopenharmony_ci{ 269cabdff1aSopenharmony_ci double sumx = 0, sumy = 0, sumx2 = 0, sumy2 = 0, sumxy = 0, 270cabdff1aSopenharmony_ci sumx_sq = 0, sumy_sq = 0, tmp, tmp2; 271cabdff1aSopenharmony_ci int i, j, count; 272cabdff1aSopenharmony_ci uint8_t x, y; 273cabdff1aSopenharmony_ci 274cabdff1aSopenharmony_ci count = bi->block_height * bi->block_width; 275cabdff1aSopenharmony_ci 276cabdff1aSopenharmony_ci if (count < 2) 277cabdff1aSopenharmony_ci return -1; 278cabdff1aSopenharmony_ci 279cabdff1aSopenharmony_ci for (i = 0; i < bi->block_height; i++) { 280cabdff1aSopenharmony_ci for (j = 0; j < bi->block_width; j++) { 281cabdff1aSopenharmony_ci x = GET_CHAN(block_ptr[j], xchannel); 282cabdff1aSopenharmony_ci y = GET_CHAN(block_ptr[j], ychannel); 283cabdff1aSopenharmony_ci sumx += x; 284cabdff1aSopenharmony_ci sumy += y; 285cabdff1aSopenharmony_ci sumx2 += x * x; 286cabdff1aSopenharmony_ci sumy2 += y * y; 287cabdff1aSopenharmony_ci sumxy += x * y; 288cabdff1aSopenharmony_ci } 289cabdff1aSopenharmony_ci block_ptr += bi->rowstride; 290cabdff1aSopenharmony_ci } 291cabdff1aSopenharmony_ci 292cabdff1aSopenharmony_ci sumx_sq = sumx * sumx; 293cabdff1aSopenharmony_ci tmp = (count * sumx2 - sumx_sq); 294cabdff1aSopenharmony_ci 295cabdff1aSopenharmony_ci // guard against div/0 296cabdff1aSopenharmony_ci if (tmp == 0) 297cabdff1aSopenharmony_ci return -2; 298cabdff1aSopenharmony_ci 299cabdff1aSopenharmony_ci sumy_sq = sumy * sumy; 300cabdff1aSopenharmony_ci 301cabdff1aSopenharmony_ci *slope = (sumx * sumy - sumxy) / tmp; 302cabdff1aSopenharmony_ci *y_intercept = (sumy - (*slope) * sumx) / count; 303cabdff1aSopenharmony_ci 304cabdff1aSopenharmony_ci tmp2 = count * sumy2 - sumy_sq; 305cabdff1aSopenharmony_ci if (tmp2 == 0) { 306cabdff1aSopenharmony_ci *correlation_coef = 0.0; 307cabdff1aSopenharmony_ci } else { 308cabdff1aSopenharmony_ci *correlation_coef = (count * sumxy - sumx * sumy) / 309cabdff1aSopenharmony_ci sqrt(tmp * tmp2); 310cabdff1aSopenharmony_ci } 311cabdff1aSopenharmony_ci 312cabdff1aSopenharmony_ci return 0; // success 313cabdff1aSopenharmony_ci} 314cabdff1aSopenharmony_ci 315cabdff1aSopenharmony_ci/* 316cabdff1aSopenharmony_ci * Determine the amount of error in the leastsquares fit. 317cabdff1aSopenharmony_ci */ 318cabdff1aSopenharmony_cistatic int calc_lsq_max_fit_error(uint16_t *block_ptr, BlockInfo *bi, 319cabdff1aSopenharmony_ci int min, int max, int tmp_min, int tmp_max, 320cabdff1aSopenharmony_ci channel_offset xchannel, channel_offset ychannel) 321cabdff1aSopenharmony_ci{ 322cabdff1aSopenharmony_ci int i, j, x, y; 323cabdff1aSopenharmony_ci int err; 324cabdff1aSopenharmony_ci int max_err = 0; 325cabdff1aSopenharmony_ci 326cabdff1aSopenharmony_ci for (i = 0; i < bi->block_height; i++) { 327cabdff1aSopenharmony_ci for (j = 0; j < bi->block_width; j++) { 328cabdff1aSopenharmony_ci int x_inc, lin_y, lin_x; 329cabdff1aSopenharmony_ci x = GET_CHAN(block_ptr[j], xchannel); 330cabdff1aSopenharmony_ci y = GET_CHAN(block_ptr[j], ychannel); 331cabdff1aSopenharmony_ci 332cabdff1aSopenharmony_ci /* calculate x_inc as the 4-color index (0..3) */ 333cabdff1aSopenharmony_ci x_inc = floor( (x - min) * 3.0 / (max - min) + 0.5); 334cabdff1aSopenharmony_ci x_inc = FFMAX(FFMIN(3, x_inc), 0); 335cabdff1aSopenharmony_ci 336cabdff1aSopenharmony_ci /* calculate lin_y corresponding to x_inc */ 337cabdff1aSopenharmony_ci lin_y = (int)(tmp_min + (tmp_max - tmp_min) * x_inc / 3.0 + 0.5); 338cabdff1aSopenharmony_ci 339cabdff1aSopenharmony_ci err = FFABS(lin_y - y); 340cabdff1aSopenharmony_ci if (err > max_err) 341cabdff1aSopenharmony_ci max_err = err; 342cabdff1aSopenharmony_ci 343cabdff1aSopenharmony_ci /* calculate lin_x corresponding to x_inc */ 344cabdff1aSopenharmony_ci lin_x = (int)(min + (max - min) * x_inc / 3.0 + 0.5); 345cabdff1aSopenharmony_ci 346cabdff1aSopenharmony_ci err = FFABS(lin_x - x); 347cabdff1aSopenharmony_ci if (err > max_err) 348cabdff1aSopenharmony_ci max_err += err; 349cabdff1aSopenharmony_ci } 350cabdff1aSopenharmony_ci block_ptr += bi->rowstride; 351cabdff1aSopenharmony_ci } 352cabdff1aSopenharmony_ci 353cabdff1aSopenharmony_ci return max_err; 354cabdff1aSopenharmony_ci} 355cabdff1aSopenharmony_ci 356cabdff1aSopenharmony_ci/* 357cabdff1aSopenharmony_ci * Find the closest match to a color within the 4-color palette 358cabdff1aSopenharmony_ci */ 359cabdff1aSopenharmony_cistatic int match_color(uint16_t *color, uint8_t colors[4][3]) 360cabdff1aSopenharmony_ci{ 361cabdff1aSopenharmony_ci int ret = 0; 362cabdff1aSopenharmony_ci int smallest_variance = INT_MAX; 363cabdff1aSopenharmony_ci uint8_t dithered_color[3]; 364cabdff1aSopenharmony_ci 365cabdff1aSopenharmony_ci for (int channel = 0; channel < 3; channel++) { 366cabdff1aSopenharmony_ci dithered_color[channel] = GET_CHAN(color[0], channel); 367cabdff1aSopenharmony_ci } 368cabdff1aSopenharmony_ci 369cabdff1aSopenharmony_ci for (int palette_entry = 0; palette_entry < 4; palette_entry++) { 370cabdff1aSopenharmony_ci int variance = diff_colors(dithered_color, colors[palette_entry]); 371cabdff1aSopenharmony_ci 372cabdff1aSopenharmony_ci if (variance < smallest_variance) { 373cabdff1aSopenharmony_ci smallest_variance = variance; 374cabdff1aSopenharmony_ci ret = palette_entry; 375cabdff1aSopenharmony_ci } 376cabdff1aSopenharmony_ci } 377cabdff1aSopenharmony_ci 378cabdff1aSopenharmony_ci return ret; 379cabdff1aSopenharmony_ci} 380cabdff1aSopenharmony_ci 381cabdff1aSopenharmony_ci/* 382cabdff1aSopenharmony_ci * Encode a block using the 4-color opcode and palette. return number of 383cabdff1aSopenharmony_ci * blocks encoded (until we implement multi-block 4 color runs this will 384cabdff1aSopenharmony_ci * always be 1) 385cabdff1aSopenharmony_ci */ 386cabdff1aSopenharmony_cistatic int encode_four_color_block(uint8_t *min_color, uint8_t *max_color, 387cabdff1aSopenharmony_ci PutBitContext *pb, uint16_t *block_ptr, BlockInfo *bi) 388cabdff1aSopenharmony_ci{ 389cabdff1aSopenharmony_ci int x, y, idx; 390cabdff1aSopenharmony_ci uint8_t color4[4][3]; 391cabdff1aSopenharmony_ci uint16_t rounded_max, rounded_min; 392cabdff1aSopenharmony_ci 393cabdff1aSopenharmony_ci // round min and max wider 394cabdff1aSopenharmony_ci rounded_min = rgb24_to_rgb555(min_color); 395cabdff1aSopenharmony_ci rounded_max = rgb24_to_rgb555(max_color); 396cabdff1aSopenharmony_ci 397cabdff1aSopenharmony_ci // put a and b colors 398cabdff1aSopenharmony_ci // encode 4 colors = first 16 bit color with MSB zeroed and... 399cabdff1aSopenharmony_ci put_bits(pb, 16, rounded_max & ~0x8000); 400cabdff1aSopenharmony_ci // ...second 16 bit color with MSB on. 401cabdff1aSopenharmony_ci put_bits(pb, 16, rounded_min | 0x8000); 402cabdff1aSopenharmony_ci 403cabdff1aSopenharmony_ci get_colors(min_color, max_color, color4); 404cabdff1aSopenharmony_ci 405cabdff1aSopenharmony_ci for (y = 0; y < 4; y++) { 406cabdff1aSopenharmony_ci for (x = 0; x < 4; x++) { 407cabdff1aSopenharmony_ci idx = match_color(&block_ptr[x], color4); 408cabdff1aSopenharmony_ci put_bits(pb, 2, idx); 409cabdff1aSopenharmony_ci } 410cabdff1aSopenharmony_ci block_ptr += bi->rowstride; 411cabdff1aSopenharmony_ci } 412cabdff1aSopenharmony_ci return 1; // num blocks encoded 413cabdff1aSopenharmony_ci} 414cabdff1aSopenharmony_ci 415cabdff1aSopenharmony_ci/* 416cabdff1aSopenharmony_ci * Copy a 4x4 block from the current frame buffer to the previous frame buffer. 417cabdff1aSopenharmony_ci */ 418cabdff1aSopenharmony_cistatic void update_block_in_prev_frame(const uint16_t *src_pixels, 419cabdff1aSopenharmony_ci uint16_t *dest_pixels, 420cabdff1aSopenharmony_ci const BlockInfo *bi, int block_counter) 421cabdff1aSopenharmony_ci{ 422cabdff1aSopenharmony_ci const int y_size = FFMIN(4, bi->image_height - bi->row * 4); 423cabdff1aSopenharmony_ci 424cabdff1aSopenharmony_ci for (int y = 0; y < y_size; y++) { 425cabdff1aSopenharmony_ci memcpy(dest_pixels, src_pixels, 8); 426cabdff1aSopenharmony_ci dest_pixels += bi->rowstride; 427cabdff1aSopenharmony_ci src_pixels += bi->rowstride; 428cabdff1aSopenharmony_ci } 429cabdff1aSopenharmony_ci} 430cabdff1aSopenharmony_ci 431cabdff1aSopenharmony_ci/* 432cabdff1aSopenharmony_ci * update statistics for the specified block. If first_block, 433cabdff1aSopenharmony_ci * it initializes the statistics. Otherwise it updates the statistics IF THIS 434cabdff1aSopenharmony_ci * BLOCK IS SUITABLE TO CONTINUE A 1-COLOR RUN. That is, it checks whether 435cabdff1aSopenharmony_ci * the range of colors (since the routine was called first_block != 0) are 436cabdff1aSopenharmony_ci * all close enough intensities to be represented by a single color. 437cabdff1aSopenharmony_ci 438cabdff1aSopenharmony_ci * The routine returns 0 if this block is too different to be part of 439cabdff1aSopenharmony_ci * the same run of 1-color blocks. The routine returns 1 if this 440cabdff1aSopenharmony_ci * block can be part of the same 1-color block run. 441cabdff1aSopenharmony_ci 442cabdff1aSopenharmony_ci * If the routine returns 1, it also updates its arguments to include 443cabdff1aSopenharmony_ci * the statistics of this block. Otherwise, the stats are unchanged 444cabdff1aSopenharmony_ci * and don't include the current block. 445cabdff1aSopenharmony_ci */ 446cabdff1aSopenharmony_cistatic int update_block_stats(RpzaContext *s, BlockInfo *bi, uint16_t *block, 447cabdff1aSopenharmony_ci uint8_t min_color[3], uint8_t max_color[3], 448cabdff1aSopenharmony_ci int *total_rgb, int *total_pixels, 449cabdff1aSopenharmony_ci uint8_t avg_color[3], int first_block) 450cabdff1aSopenharmony_ci{ 451cabdff1aSopenharmony_ci int x, y; 452cabdff1aSopenharmony_ci int is_in_range; 453cabdff1aSopenharmony_ci int total_pixels_blk; 454cabdff1aSopenharmony_ci int threshold; 455cabdff1aSopenharmony_ci 456cabdff1aSopenharmony_ci uint8_t min_color_blk[3], max_color_blk[3]; 457cabdff1aSopenharmony_ci int total_rgb_blk[3]; 458cabdff1aSopenharmony_ci uint8_t avg_color_blk[3]; 459cabdff1aSopenharmony_ci 460cabdff1aSopenharmony_ci if (first_block) { 461cabdff1aSopenharmony_ci min_color[0] = UINT8_MAX; 462cabdff1aSopenharmony_ci min_color[1] = UINT8_MAX; 463cabdff1aSopenharmony_ci min_color[2] = UINT8_MAX; 464cabdff1aSopenharmony_ci max_color[0] = 0; 465cabdff1aSopenharmony_ci max_color[1] = 0; 466cabdff1aSopenharmony_ci max_color[2] = 0; 467cabdff1aSopenharmony_ci total_rgb[0] = 0; 468cabdff1aSopenharmony_ci total_rgb[1] = 0; 469cabdff1aSopenharmony_ci total_rgb[2] = 0; 470cabdff1aSopenharmony_ci *total_pixels = 0; 471cabdff1aSopenharmony_ci threshold = s->start_one_color_thresh; 472cabdff1aSopenharmony_ci } else { 473cabdff1aSopenharmony_ci threshold = s->continue_one_color_thresh; 474cabdff1aSopenharmony_ci } 475cabdff1aSopenharmony_ci 476cabdff1aSopenharmony_ci /* 477cabdff1aSopenharmony_ci The *_blk variables will include the current block. 478cabdff1aSopenharmony_ci Initialize them based on the blocks so far. 479cabdff1aSopenharmony_ci */ 480cabdff1aSopenharmony_ci min_color_blk[0] = min_color[0]; 481cabdff1aSopenharmony_ci min_color_blk[1] = min_color[1]; 482cabdff1aSopenharmony_ci min_color_blk[2] = min_color[2]; 483cabdff1aSopenharmony_ci max_color_blk[0] = max_color[0]; 484cabdff1aSopenharmony_ci max_color_blk[1] = max_color[1]; 485cabdff1aSopenharmony_ci max_color_blk[2] = max_color[2]; 486cabdff1aSopenharmony_ci total_rgb_blk[0] = total_rgb[0]; 487cabdff1aSopenharmony_ci total_rgb_blk[1] = total_rgb[1]; 488cabdff1aSopenharmony_ci total_rgb_blk[2] = total_rgb[2]; 489cabdff1aSopenharmony_ci total_pixels_blk = *total_pixels + bi->block_height * bi->block_width; 490cabdff1aSopenharmony_ci 491cabdff1aSopenharmony_ci /* 492cabdff1aSopenharmony_ci Update stats for this block's pixels 493cabdff1aSopenharmony_ci */ 494cabdff1aSopenharmony_ci for (y = 0; y < bi->block_height; y++) { 495cabdff1aSopenharmony_ci for (x = 0; x < bi->block_width; x++) { 496cabdff1aSopenharmony_ci total_rgb_blk[0] += R(block[x]); 497cabdff1aSopenharmony_ci total_rgb_blk[1] += G(block[x]); 498cabdff1aSopenharmony_ci total_rgb_blk[2] += B(block[x]); 499cabdff1aSopenharmony_ci 500cabdff1aSopenharmony_ci min_color_blk[0] = FFMIN(R(block[x]), min_color_blk[0]); 501cabdff1aSopenharmony_ci min_color_blk[1] = FFMIN(G(block[x]), min_color_blk[1]); 502cabdff1aSopenharmony_ci min_color_blk[2] = FFMIN(B(block[x]), min_color_blk[2]); 503cabdff1aSopenharmony_ci 504cabdff1aSopenharmony_ci max_color_blk[0] = FFMAX(R(block[x]), max_color_blk[0]); 505cabdff1aSopenharmony_ci max_color_blk[1] = FFMAX(G(block[x]), max_color_blk[1]); 506cabdff1aSopenharmony_ci max_color_blk[2] = FFMAX(B(block[x]), max_color_blk[2]); 507cabdff1aSopenharmony_ci } 508cabdff1aSopenharmony_ci block += bi->rowstride; 509cabdff1aSopenharmony_ci } 510cabdff1aSopenharmony_ci 511cabdff1aSopenharmony_ci /* 512cabdff1aSopenharmony_ci Calculate average color including current block. 513cabdff1aSopenharmony_ci */ 514cabdff1aSopenharmony_ci avg_color_blk[0] = total_rgb_blk[0] / total_pixels_blk; 515cabdff1aSopenharmony_ci avg_color_blk[1] = total_rgb_blk[1] / total_pixels_blk; 516cabdff1aSopenharmony_ci avg_color_blk[2] = total_rgb_blk[2] / total_pixels_blk; 517cabdff1aSopenharmony_ci 518cabdff1aSopenharmony_ci /* 519cabdff1aSopenharmony_ci Are all the pixels within threshold of the average color? 520cabdff1aSopenharmony_ci */ 521cabdff1aSopenharmony_ci is_in_range = (max_color_blk[0] - avg_color_blk[0] <= threshold && 522cabdff1aSopenharmony_ci max_color_blk[1] - avg_color_blk[1] <= threshold && 523cabdff1aSopenharmony_ci max_color_blk[2] - avg_color_blk[2] <= threshold && 524cabdff1aSopenharmony_ci avg_color_blk[0] - min_color_blk[0] <= threshold && 525cabdff1aSopenharmony_ci avg_color_blk[1] - min_color_blk[1] <= threshold && 526cabdff1aSopenharmony_ci avg_color_blk[2] - min_color_blk[2] <= threshold); 527cabdff1aSopenharmony_ci 528cabdff1aSopenharmony_ci if (is_in_range) { 529cabdff1aSopenharmony_ci /* 530cabdff1aSopenharmony_ci Set the output variables to include this block. 531cabdff1aSopenharmony_ci */ 532cabdff1aSopenharmony_ci min_color[0] = min_color_blk[0]; 533cabdff1aSopenharmony_ci min_color[1] = min_color_blk[1]; 534cabdff1aSopenharmony_ci min_color[2] = min_color_blk[2]; 535cabdff1aSopenharmony_ci max_color[0] = max_color_blk[0]; 536cabdff1aSopenharmony_ci max_color[1] = max_color_blk[1]; 537cabdff1aSopenharmony_ci max_color[2] = max_color_blk[2]; 538cabdff1aSopenharmony_ci total_rgb[0] = total_rgb_blk[0]; 539cabdff1aSopenharmony_ci total_rgb[1] = total_rgb_blk[1]; 540cabdff1aSopenharmony_ci total_rgb[2] = total_rgb_blk[2]; 541cabdff1aSopenharmony_ci *total_pixels = total_pixels_blk; 542cabdff1aSopenharmony_ci avg_color[0] = avg_color_blk[0]; 543cabdff1aSopenharmony_ci avg_color[1] = avg_color_blk[1]; 544cabdff1aSopenharmony_ci avg_color[2] = avg_color_blk[2]; 545cabdff1aSopenharmony_ci } 546cabdff1aSopenharmony_ci 547cabdff1aSopenharmony_ci return is_in_range; 548cabdff1aSopenharmony_ci} 549cabdff1aSopenharmony_ci 550cabdff1aSopenharmony_cistatic void rpza_encode_stream(RpzaContext *s, const AVFrame *pict) 551cabdff1aSopenharmony_ci{ 552cabdff1aSopenharmony_ci BlockInfo bi; 553cabdff1aSopenharmony_ci int block_counter = 0; 554cabdff1aSopenharmony_ci int n_blocks; 555cabdff1aSopenharmony_ci int total_blocks; 556cabdff1aSopenharmony_ci int prev_block_offset; 557cabdff1aSopenharmony_ci int block_offset = 0; 558cabdff1aSopenharmony_ci uint8_t min = 0, max = 0; 559cabdff1aSopenharmony_ci channel_offset chan; 560cabdff1aSopenharmony_ci int i; 561cabdff1aSopenharmony_ci int tmp_min, tmp_max; 562cabdff1aSopenharmony_ci int total_rgb[3]; 563cabdff1aSopenharmony_ci uint8_t avg_color[3]; 564cabdff1aSopenharmony_ci int pixel_count; 565cabdff1aSopenharmony_ci uint8_t min_color[3], max_color[3]; 566cabdff1aSopenharmony_ci double slope, y_intercept, correlation_coef; 567cabdff1aSopenharmony_ci uint16_t *src_pixels = (uint16_t *)pict->data[0]; 568cabdff1aSopenharmony_ci uint16_t *prev_pixels = (uint16_t *)s->prev_frame->data[0]; 569cabdff1aSopenharmony_ci 570cabdff1aSopenharmony_ci /* Number of 4x4 blocks in frame. */ 571cabdff1aSopenharmony_ci total_blocks = ((s->frame_width + 3) / 4) * ((s->frame_height + 3) / 4); 572cabdff1aSopenharmony_ci 573cabdff1aSopenharmony_ci bi.image_width = s->frame_width; 574cabdff1aSopenharmony_ci bi.image_height = s->frame_height; 575cabdff1aSopenharmony_ci bi.rowstride = pict->linesize[0] / 2; 576cabdff1aSopenharmony_ci 577cabdff1aSopenharmony_ci bi.blocks_per_row = (s->frame_width + 3) / 4; 578cabdff1aSopenharmony_ci 579cabdff1aSopenharmony_ci while (block_counter < total_blocks) { 580cabdff1aSopenharmony_ci // SKIP CHECK 581cabdff1aSopenharmony_ci // make sure we have a valid previous frame and we're not writing 582cabdff1aSopenharmony_ci // a key frame 583cabdff1aSopenharmony_ci if (!s->first_frame) { 584cabdff1aSopenharmony_ci n_blocks = 0; 585cabdff1aSopenharmony_ci prev_block_offset = 0; 586cabdff1aSopenharmony_ci 587cabdff1aSopenharmony_ci while (n_blocks < 32 && block_counter + n_blocks < total_blocks) { 588cabdff1aSopenharmony_ci 589cabdff1aSopenharmony_ci block_offset = get_block_info(&bi, block_counter + n_blocks); 590cabdff1aSopenharmony_ci 591cabdff1aSopenharmony_ci // multi-block opcodes cannot span multiple rows. 592cabdff1aSopenharmony_ci // If we're starting a new row, break out and write the opcode 593cabdff1aSopenharmony_ci /* TODO: Should eventually use bi.row here to determine when a 594cabdff1aSopenharmony_ci row break occurs, but that is currently breaking the 595cabdff1aSopenharmony_ci quicktime player. This is probably due to a bug in the 596cabdff1aSopenharmony_ci way I'm calculating the current row. 597cabdff1aSopenharmony_ci */ 598cabdff1aSopenharmony_ci if (prev_block_offset && block_offset - prev_block_offset > 12) { 599cabdff1aSopenharmony_ci break; 600cabdff1aSopenharmony_ci } 601cabdff1aSopenharmony_ci 602cabdff1aSopenharmony_ci prev_block_offset = block_offset; 603cabdff1aSopenharmony_ci 604cabdff1aSopenharmony_ci if (compare_blocks(&prev_pixels[block_offset], 605cabdff1aSopenharmony_ci &src_pixels[block_offset], &bi, s->skip_frame_thresh) != 0) { 606cabdff1aSopenharmony_ci // write out skipable blocks 607cabdff1aSopenharmony_ci if (n_blocks) { 608cabdff1aSopenharmony_ci 609cabdff1aSopenharmony_ci // write skip opcode 610cabdff1aSopenharmony_ci put_bits(&s->pb, 8, 0x80 | (n_blocks - 1)); 611cabdff1aSopenharmony_ci block_counter += n_blocks; 612cabdff1aSopenharmony_ci 613cabdff1aSopenharmony_ci goto post_skip; 614cabdff1aSopenharmony_ci } 615cabdff1aSopenharmony_ci break; 616cabdff1aSopenharmony_ci } 617cabdff1aSopenharmony_ci 618cabdff1aSopenharmony_ci /* 619cabdff1aSopenharmony_ci * NOTE: we don't update skipped blocks in the previous frame buffer 620cabdff1aSopenharmony_ci * since skipped needs always to be compared against the first skipped 621cabdff1aSopenharmony_ci * block to avoid artifacts during gradual fade in/outs. 622cabdff1aSopenharmony_ci */ 623cabdff1aSopenharmony_ci 624cabdff1aSopenharmony_ci // update_block_in_prev_frame(&src_pixels[block_offset], 625cabdff1aSopenharmony_ci // &prev_pixels[block_offset], &bi, block_counter + n_blocks); 626cabdff1aSopenharmony_ci 627cabdff1aSopenharmony_ci n_blocks++; 628cabdff1aSopenharmony_ci } 629cabdff1aSopenharmony_ci 630cabdff1aSopenharmony_ci // we're either at the end of the frame or we've reached the maximum 631cabdff1aSopenharmony_ci // of 32 blocks in a run. Write out the run. 632cabdff1aSopenharmony_ci if (n_blocks) { 633cabdff1aSopenharmony_ci // write skip opcode 634cabdff1aSopenharmony_ci put_bits(&s->pb, 8, 0x80 | (n_blocks - 1)); 635cabdff1aSopenharmony_ci block_counter += n_blocks; 636cabdff1aSopenharmony_ci 637cabdff1aSopenharmony_ci continue; 638cabdff1aSopenharmony_ci } 639cabdff1aSopenharmony_ci 640cabdff1aSopenharmony_ci } else { 641cabdff1aSopenharmony_ci block_offset = get_block_info(&bi, block_counter); 642cabdff1aSopenharmony_ci } 643cabdff1aSopenharmony_cipost_skip : 644cabdff1aSopenharmony_ci 645cabdff1aSopenharmony_ci // ONE COLOR CHECK 646cabdff1aSopenharmony_ci if (update_block_stats(s, &bi, &src_pixels[block_offset], 647cabdff1aSopenharmony_ci min_color, max_color, 648cabdff1aSopenharmony_ci total_rgb, &pixel_count, avg_color, 1)) { 649cabdff1aSopenharmony_ci prev_block_offset = block_offset; 650cabdff1aSopenharmony_ci 651cabdff1aSopenharmony_ci n_blocks = 1; 652cabdff1aSopenharmony_ci 653cabdff1aSopenharmony_ci /* update this block in the previous frame buffer */ 654cabdff1aSopenharmony_ci update_block_in_prev_frame(&src_pixels[block_offset], 655cabdff1aSopenharmony_ci &prev_pixels[block_offset], &bi, block_counter + n_blocks); 656cabdff1aSopenharmony_ci 657cabdff1aSopenharmony_ci // check for subsequent blocks with the same color 658cabdff1aSopenharmony_ci while (n_blocks < 32 && block_counter + n_blocks < total_blocks) { 659cabdff1aSopenharmony_ci block_offset = get_block_info(&bi, block_counter + n_blocks); 660cabdff1aSopenharmony_ci 661cabdff1aSopenharmony_ci // multi-block opcodes cannot span multiple rows. 662cabdff1aSopenharmony_ci // If we've hit end of a row, break out and write the opcode 663cabdff1aSopenharmony_ci if (block_offset - prev_block_offset > 12) { 664cabdff1aSopenharmony_ci break; 665cabdff1aSopenharmony_ci } 666cabdff1aSopenharmony_ci 667cabdff1aSopenharmony_ci if (!update_block_stats(s, &bi, &src_pixels[block_offset], 668cabdff1aSopenharmony_ci min_color, max_color, 669cabdff1aSopenharmony_ci total_rgb, &pixel_count, avg_color, 0)) { 670cabdff1aSopenharmony_ci break; 671cabdff1aSopenharmony_ci } 672cabdff1aSopenharmony_ci 673cabdff1aSopenharmony_ci prev_block_offset = block_offset; 674cabdff1aSopenharmony_ci 675cabdff1aSopenharmony_ci /* update this block in the previous frame buffer */ 676cabdff1aSopenharmony_ci update_block_in_prev_frame(&src_pixels[block_offset], 677cabdff1aSopenharmony_ci &prev_pixels[block_offset], &bi, block_counter + n_blocks); 678cabdff1aSopenharmony_ci 679cabdff1aSopenharmony_ci n_blocks++; 680cabdff1aSopenharmony_ci } 681cabdff1aSopenharmony_ci 682cabdff1aSopenharmony_ci // write one color opcode. 683cabdff1aSopenharmony_ci put_bits(&s->pb, 8, 0xa0 | (n_blocks - 1)); 684cabdff1aSopenharmony_ci // write color to encode. 685cabdff1aSopenharmony_ci put_bits(&s->pb, 16, rgb24_to_rgb555(avg_color)); 686cabdff1aSopenharmony_ci // skip past the blocks we've just encoded. 687cabdff1aSopenharmony_ci block_counter += n_blocks; 688cabdff1aSopenharmony_ci } else { // FOUR COLOR CHECK 689cabdff1aSopenharmony_ci int err = 0; 690cabdff1aSopenharmony_ci 691cabdff1aSopenharmony_ci // get max component diff for block 692cabdff1aSopenharmony_ci get_max_component_diff(&bi, &src_pixels[block_offset], &min, &max, &chan); 693cabdff1aSopenharmony_ci 694cabdff1aSopenharmony_ci min_color[0] = 0; 695cabdff1aSopenharmony_ci max_color[0] = 0; 696cabdff1aSopenharmony_ci min_color[1] = 0; 697cabdff1aSopenharmony_ci max_color[1] = 0; 698cabdff1aSopenharmony_ci min_color[2] = 0; 699cabdff1aSopenharmony_ci max_color[2] = 0; 700cabdff1aSopenharmony_ci 701cabdff1aSopenharmony_ci // run least squares against other two components 702cabdff1aSopenharmony_ci for (i = 0; i < 3; i++) { 703cabdff1aSopenharmony_ci if (i == chan) { 704cabdff1aSopenharmony_ci min_color[i] = min; 705cabdff1aSopenharmony_ci max_color[i] = max; 706cabdff1aSopenharmony_ci continue; 707cabdff1aSopenharmony_ci } 708cabdff1aSopenharmony_ci 709cabdff1aSopenharmony_ci slope = y_intercept = correlation_coef = 0; 710cabdff1aSopenharmony_ci 711cabdff1aSopenharmony_ci if (leastsquares(&src_pixels[block_offset], &bi, chan, i, 712cabdff1aSopenharmony_ci &slope, &y_intercept, &correlation_coef)) { 713cabdff1aSopenharmony_ci min_color[i] = GET_CHAN(src_pixels[block_offset], i); 714cabdff1aSopenharmony_ci max_color[i] = GET_CHAN(src_pixels[block_offset], i); 715cabdff1aSopenharmony_ci } else { 716cabdff1aSopenharmony_ci tmp_min = (int)(0.5 + min * slope + y_intercept); 717cabdff1aSopenharmony_ci tmp_max = (int)(0.5 + max * slope + y_intercept); 718cabdff1aSopenharmony_ci 719cabdff1aSopenharmony_ci av_assert0(tmp_min <= tmp_max); 720cabdff1aSopenharmony_ci // clamp min and max color values 721cabdff1aSopenharmony_ci tmp_min = av_clip_uint8(tmp_min); 722cabdff1aSopenharmony_ci tmp_max = av_clip_uint8(tmp_max); 723cabdff1aSopenharmony_ci 724cabdff1aSopenharmony_ci err = FFMAX(calc_lsq_max_fit_error(&src_pixels[block_offset], &bi, 725cabdff1aSopenharmony_ci min, max, tmp_min, tmp_max, chan, i), err); 726cabdff1aSopenharmony_ci 727cabdff1aSopenharmony_ci min_color[i] = tmp_min; 728cabdff1aSopenharmony_ci max_color[i] = tmp_max; 729cabdff1aSopenharmony_ci } 730cabdff1aSopenharmony_ci } 731cabdff1aSopenharmony_ci 732cabdff1aSopenharmony_ci if (err > s->sixteen_color_thresh) { // DO SIXTEEN COLOR BLOCK 733cabdff1aSopenharmony_ci uint16_t *row_ptr; 734cabdff1aSopenharmony_ci int y_size, x_size, rgb555; 735cabdff1aSopenharmony_ci 736cabdff1aSopenharmony_ci block_offset = get_block_info(&bi, block_counter); 737cabdff1aSopenharmony_ci 738cabdff1aSopenharmony_ci row_ptr = &src_pixels[block_offset]; 739cabdff1aSopenharmony_ci y_size = FFMIN(4, bi.image_height - bi.row * 4); 740cabdff1aSopenharmony_ci x_size = FFMIN(4, bi.image_width - bi.col * 4); 741cabdff1aSopenharmony_ci 742cabdff1aSopenharmony_ci for (int y = 0; y < y_size; y++) { 743cabdff1aSopenharmony_ci for (int x = 0; x < x_size; x++) { 744cabdff1aSopenharmony_ci rgb555 = row_ptr[x] & ~0x8000; 745cabdff1aSopenharmony_ci 746cabdff1aSopenharmony_ci put_bits(&s->pb, 16, rgb555); 747cabdff1aSopenharmony_ci } 748cabdff1aSopenharmony_ci for (int x = x_size; x < 4; x++) 749cabdff1aSopenharmony_ci put_bits(&s->pb, 16, 0); 750cabdff1aSopenharmony_ci row_ptr += bi.rowstride; 751cabdff1aSopenharmony_ci } 752cabdff1aSopenharmony_ci 753cabdff1aSopenharmony_ci for (int y = y_size; y < 4; y++) { 754cabdff1aSopenharmony_ci for (int x = 0; x < 4; x++) 755cabdff1aSopenharmony_ci put_bits(&s->pb, 16, 0); 756cabdff1aSopenharmony_ci } 757cabdff1aSopenharmony_ci 758cabdff1aSopenharmony_ci block_counter++; 759cabdff1aSopenharmony_ci } else { // FOUR COLOR BLOCK 760cabdff1aSopenharmony_ci block_counter += encode_four_color_block(min_color, max_color, 761cabdff1aSopenharmony_ci &s->pb, &src_pixels[block_offset], &bi); 762cabdff1aSopenharmony_ci } 763cabdff1aSopenharmony_ci 764cabdff1aSopenharmony_ci /* update this block in the previous frame buffer */ 765cabdff1aSopenharmony_ci update_block_in_prev_frame(&src_pixels[block_offset], 766cabdff1aSopenharmony_ci &prev_pixels[block_offset], &bi, block_counter); 767cabdff1aSopenharmony_ci } 768cabdff1aSopenharmony_ci } 769cabdff1aSopenharmony_ci} 770cabdff1aSopenharmony_ci 771cabdff1aSopenharmony_cistatic int rpza_encode_init(AVCodecContext *avctx) 772cabdff1aSopenharmony_ci{ 773cabdff1aSopenharmony_ci RpzaContext *s = avctx->priv_data; 774cabdff1aSopenharmony_ci 775cabdff1aSopenharmony_ci s->frame_width = avctx->width; 776cabdff1aSopenharmony_ci s->frame_height = avctx->height; 777cabdff1aSopenharmony_ci 778cabdff1aSopenharmony_ci s->prev_frame = av_frame_alloc(); 779cabdff1aSopenharmony_ci if (!s->prev_frame) 780cabdff1aSopenharmony_ci return AVERROR(ENOMEM); 781cabdff1aSopenharmony_ci 782cabdff1aSopenharmony_ci return 0; 783cabdff1aSopenharmony_ci} 784cabdff1aSopenharmony_ci 785cabdff1aSopenharmony_cistatic int rpza_encode_frame(AVCodecContext *avctx, AVPacket *pkt, 786cabdff1aSopenharmony_ci const AVFrame *frame, int *got_packet) 787cabdff1aSopenharmony_ci{ 788cabdff1aSopenharmony_ci RpzaContext *s = avctx->priv_data; 789cabdff1aSopenharmony_ci const AVFrame *pict = frame; 790cabdff1aSopenharmony_ci uint8_t *buf; 791cabdff1aSopenharmony_ci int ret = ff_alloc_packet(avctx, pkt, 6LL * avctx->height * avctx->width); 792cabdff1aSopenharmony_ci 793cabdff1aSopenharmony_ci if (ret < 0) 794cabdff1aSopenharmony_ci return ret; 795cabdff1aSopenharmony_ci 796cabdff1aSopenharmony_ci init_put_bits(&s->pb, pkt->data, pkt->size); 797cabdff1aSopenharmony_ci 798cabdff1aSopenharmony_ci // skip 4 byte header, write it later once the size of the chunk is known 799cabdff1aSopenharmony_ci put_bits32(&s->pb, 0x00); 800cabdff1aSopenharmony_ci 801cabdff1aSopenharmony_ci if (!s->prev_frame->data[0]) { 802cabdff1aSopenharmony_ci s->first_frame = 1; 803cabdff1aSopenharmony_ci s->prev_frame->format = pict->format; 804cabdff1aSopenharmony_ci s->prev_frame->width = pict->width; 805cabdff1aSopenharmony_ci s->prev_frame->height = pict->height; 806cabdff1aSopenharmony_ci ret = av_frame_get_buffer(s->prev_frame, 0); 807cabdff1aSopenharmony_ci if (ret < 0) 808cabdff1aSopenharmony_ci return ret; 809cabdff1aSopenharmony_ci } else { 810cabdff1aSopenharmony_ci s->first_frame = 0; 811cabdff1aSopenharmony_ci } 812cabdff1aSopenharmony_ci 813cabdff1aSopenharmony_ci rpza_encode_stream(s, pict); 814cabdff1aSopenharmony_ci 815cabdff1aSopenharmony_ci flush_put_bits(&s->pb); 816cabdff1aSopenharmony_ci 817cabdff1aSopenharmony_ci av_shrink_packet(pkt, put_bytes_output(&s->pb)); 818cabdff1aSopenharmony_ci buf = pkt->data; 819cabdff1aSopenharmony_ci 820cabdff1aSopenharmony_ci // write header opcode 821cabdff1aSopenharmony_ci buf[0] = 0xe1; // chunk opcode 822cabdff1aSopenharmony_ci 823cabdff1aSopenharmony_ci // write chunk length 824cabdff1aSopenharmony_ci AV_WB24(buf + 1, pkt->size); 825cabdff1aSopenharmony_ci 826cabdff1aSopenharmony_ci *got_packet = 1; 827cabdff1aSopenharmony_ci 828cabdff1aSopenharmony_ci return 0; 829cabdff1aSopenharmony_ci} 830cabdff1aSopenharmony_ci 831cabdff1aSopenharmony_cistatic int rpza_encode_end(AVCodecContext *avctx) 832cabdff1aSopenharmony_ci{ 833cabdff1aSopenharmony_ci RpzaContext *s = (RpzaContext *)avctx->priv_data; 834cabdff1aSopenharmony_ci 835cabdff1aSopenharmony_ci av_frame_free(&s->prev_frame); 836cabdff1aSopenharmony_ci 837cabdff1aSopenharmony_ci return 0; 838cabdff1aSopenharmony_ci} 839cabdff1aSopenharmony_ci 840cabdff1aSopenharmony_ci#define OFFSET(x) offsetof(RpzaContext, x) 841cabdff1aSopenharmony_ci#define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM 842cabdff1aSopenharmony_cistatic const AVOption options[] = { 843cabdff1aSopenharmony_ci { "skip_frame_thresh", NULL, OFFSET(skip_frame_thresh), AV_OPT_TYPE_INT, {.i64=1}, 0, 24, VE}, 844cabdff1aSopenharmony_ci { "start_one_color_thresh", NULL, OFFSET(start_one_color_thresh), AV_OPT_TYPE_INT, {.i64=1}, 0, 24, VE}, 845cabdff1aSopenharmony_ci { "continue_one_color_thresh", NULL, OFFSET(continue_one_color_thresh), AV_OPT_TYPE_INT, {.i64=0}, 0, 24, VE}, 846cabdff1aSopenharmony_ci { "sixteen_color_thresh", NULL, OFFSET(sixteen_color_thresh), AV_OPT_TYPE_INT, {.i64=1}, 0, 24, VE}, 847cabdff1aSopenharmony_ci { NULL }, 848cabdff1aSopenharmony_ci}; 849cabdff1aSopenharmony_ci 850cabdff1aSopenharmony_cistatic const AVClass rpza_class = { 851cabdff1aSopenharmony_ci .class_name = "rpza", 852cabdff1aSopenharmony_ci .item_name = av_default_item_name, 853cabdff1aSopenharmony_ci .option = options, 854cabdff1aSopenharmony_ci .version = LIBAVUTIL_VERSION_INT, 855cabdff1aSopenharmony_ci}; 856cabdff1aSopenharmony_ci 857cabdff1aSopenharmony_ciconst FFCodec ff_rpza_encoder = { 858cabdff1aSopenharmony_ci .p.name = "rpza", 859cabdff1aSopenharmony_ci .p.long_name = NULL_IF_CONFIG_SMALL("QuickTime video (RPZA)"), 860cabdff1aSopenharmony_ci .p.type = AVMEDIA_TYPE_VIDEO, 861cabdff1aSopenharmony_ci .p.id = AV_CODEC_ID_RPZA, 862cabdff1aSopenharmony_ci .priv_data_size = sizeof(RpzaContext), 863cabdff1aSopenharmony_ci .p.priv_class = &rpza_class, 864cabdff1aSopenharmony_ci .init = rpza_encode_init, 865cabdff1aSopenharmony_ci FF_CODEC_ENCODE_CB(rpza_encode_frame), 866cabdff1aSopenharmony_ci .close = rpza_encode_end, 867cabdff1aSopenharmony_ci .caps_internal = FF_CODEC_CAP_INIT_THREADSAFE, 868cabdff1aSopenharmony_ci .p.pix_fmts = (const enum AVPixelFormat[]) { AV_PIX_FMT_RGB555, 869cabdff1aSopenharmony_ci AV_PIX_FMT_NONE}, 870cabdff1aSopenharmony_ci}; 871