From fc9dbe4bc35cdcd21b061e90f5097b05473a9c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mike=20Kr=C3=BCger?= Date: Sat, 9 Sep 2023 16:20:28 +0200 Subject: [PATCH] Ported mediancut. --- Cargo.toml | 2 +- src/dither.rs | 85 +- src/lib.rs | 28 +- src/quant.rs | 2146 +++++++++++++++++++++++------------------------- src/tosixel.rs | 322 ++++---- 5 files changed, 1237 insertions(+), 1346 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 7cc7b6b..7c49add 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "icy_sixel" -version = "0.1.0" +version = "0.1.1" edition = "2021" authors = ["Mike Krüger "] description = "A 100% rust sixel library." diff --git a/src/dither.rs b/src/dither.rs index dfcceb4..240fdc8 100644 --- a/src/dither.rs +++ b/src/dither.rs @@ -1,9 +1,10 @@ use std::vec; use crate::{ - pixelformat::sixel_helper_normalize_pixelformat, BuiltinDither, ColorChoosingMethod, - DiffusionMethod, FindLargestDim, PixelFormat, Quality, SixelError, SixelResult, - SIXEL_PALETTE_MAX, quant::{sixel_quant_make_palette, sixel_quant_apply_palette}, + pixelformat::sixel_helper_normalize_pixelformat, + quant::{sixel_quant_apply_palette, sixel_quant_make_palette}, + BuiltinDither, DiffusionMethod, MethodForLargest, MethodForRep, PixelFormat, Quality, + SixelError, SixelResult, SIXEL_PALETTE_MAX, }; pub struct sixel_dither { @@ -16,13 +17,13 @@ pub struct sixel_dither { pub optimize_palette: bool, /* minimize palette size */ pub complexion: i32, /* for complexion correction */ pub bodyonly: bool, /* do not output palette section if true */ - pub method_for_largest: FindLargestDim, /* method for finding the largest dimention + pub method_for_largest: MethodForLargest, /* method for finding the largest dimention for splitting */ - pub method_for_rep: ColorChoosingMethod, /* method for choosing a color from the box */ + pub method_for_rep: MethodForRep, /* method for choosing a color from the box */ pub method_for_diffuse: DiffusionMethod, /* method for diffusing */ - pub quality_mode: Quality, /* quality of histogram */ - pub keycolor: i32, /* background color */ - pub pixelformat: PixelFormat, /* pixelformat for internal processing */ + pub quality_mode: Quality, /* quality of histogram */ + pub keycolor: i32, /* background color */ + pub pixelformat: PixelFormat, /* pixelformat for internal processing */ } const pal_mono_dark: [u8; 6] = [0x00, 0x00, 0x00, 0xff, 0xff, 0xff]; @@ -205,7 +206,6 @@ impl sixel_dither { } Quality::LOW }; - Ok(Self { palette: vec![0; ncolors as usize * 3], cachetable: None, @@ -217,8 +217,8 @@ impl sixel_dither { optimize_palette: false, complexion: 1, bodyonly: false, - method_for_largest: FindLargestDim::Norm, - method_for_rep: ColorChoosingMethod::CenterBox, + method_for_largest: MethodForLargest::Norm, + method_for_rep: MethodForRep::CenterBox, method_for_diffuse: DiffusionMethod::FS, quality_mode, pixelformat: PixelFormat::RGB888, @@ -247,17 +247,17 @@ impl sixel_dither { Ok(result) } - pub fn set_method_for_largest(&mut self, method_for_largest: FindLargestDim) { - self.method_for_largest = if matches!(method_for_largest, FindLargestDim::Auto) { - FindLargestDim::Norm + pub fn set_method_for_largest(&mut self, method_for_largest: MethodForLargest) { + self.method_for_largest = if matches!(method_for_largest, MethodForLargest::Auto) { + MethodForLargest::Norm } else { method_for_largest }; } - pub fn set_method_for_rep(&mut self, method_for_rep: ColorChoosingMethod) { - self.method_for_rep = if matches!(method_for_rep, ColorChoosingMethod::Auto) { - ColorChoosingMethod::CenterBox + pub fn set_method_for_rep(&mut self, method_for_rep: MethodForRep) { + self.method_for_rep = if matches!(method_for_rep, MethodForRep::Auto) { + MethodForRep::CenterBox } else { method_for_rep }; @@ -281,8 +281,8 @@ impl sixel_dither { width: i32, height: i32, mut pixelformat: PixelFormat, - method_for_largest: FindLargestDim, - method_for_rep: ColorChoosingMethod, + method_for_largest: MethodForLargest, + method_for_rep: MethodForRep, quality_mode: Quality, ) -> SixelResult<()> { self.set_pixelformat(pixelformat); @@ -307,17 +307,17 @@ impl sixel_dither { self.set_quality_mode(quality_mode); let buf = sixel_quant_make_palette( - &input_pixels, - width * height * 3, - PixelFormat::RGB888, - self.reqcolors, - &mut self.ncolors, - &mut self.origcolors, - self.method_for_largest, - self.method_for_rep, - self.quality_mode)?; - - println!("set colors:{}", self.ncolors); + &input_pixels, + width * height * 3, + PixelFormat::RGB888, + self.reqcolors, + &mut self.ncolors, + &mut self.origcolors, + self.method_for_largest, + self.method_for_rep, + self.quality_mode, + )?; + self.palette = buf; self.optimized = true; if self.origcolors <= self.ncolors { @@ -430,17 +430,20 @@ impl sixel_dither { } else { pixels.to_vec() }; - - let ncolors = sixel_quant_apply_palette(&mut dest, - &mut input_pixels, - width, height, 3, - &mut self.palette, - self.ncolors, - self.method_for_diffuse, - self.optimized, - self.optimize_palette, - self.complexion, - Some(self.cachetable.as_mut().unwrap()))?; + let ncolors = sixel_quant_apply_palette( + &mut dest, + &mut input_pixels, + width, + height, + 3, + &mut self.palette, + self.ncolors, + self.method_for_diffuse, + self.optimized, + self.optimize_palette, + self.complexion, + Some(self.cachetable.as_mut().unwrap()), + )?; self.ncolors = ncolors; Ok(dest) diff --git a/src/lib.rs b/src/lib.rs index 5fceccf..aded59f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,8 +6,8 @@ use output::sixel_output; pub mod dither; pub mod output; pub mod pixelformat; -pub mod tosixel; pub mod quant; +pub mod tosixel; /* limitations */ const SIXEL_OUTPUT_PACKET_SIZE: usize = 16384; @@ -95,7 +95,7 @@ const SIXEL_FAILED(status) (((status) & 0x1000) != 0) /// method for finding the largest dimension for splitting, /// and sorting by that component #[derive(Clone, Copy)] -pub enum FindLargestDim { +pub enum MethodForLargest { /// choose automatically the method for finding the largest dimension Auto, /// simply comparing the range in RGB space @@ -106,7 +106,7 @@ pub enum FindLargestDim { /// method for choosing a color from the box #[derive(Clone, Copy)] -pub enum ColorChoosingMethod { +pub enum MethodForRep { /// choose automatically the method for selecting /// representative color from each box Auto, @@ -592,6 +592,9 @@ pub fn sixel_string( height: i32, pixelformat: PixelFormat, method_for_diffuse: DiffusionMethod, + method_for_largest: MethodForLargest, + method_for_rep: MethodForRep, + quality_mode: Quality, ) -> SixelResult { let mut sixel_data: Vec = Vec::new(); @@ -604,16 +607,21 @@ pub fn sixel_string( width, height, pixelformat, - FindLargestDim::Norm, - ColorChoosingMethod::Auto, - Quality::AUTO, + method_for_largest, + method_for_rep, + quality_mode, )?; sixel_dither.set_pixelformat(pixelformat); - sixel_dither.set_diffusion_type(DiffusionMethod::Stucki); - // sixel_dither.set_diffusion_type(method_for_diffuse); - + sixel_dither.set_diffusion_type(method_for_diffuse); + let mut bytes = bytes.to_vec(); sixel_output.encode(&mut bytes, width, height, 0, &mut sixel_dither)?; Ok(String::from_utf8_lossy(&sixel_data).to_string()) -} +} /* + pub fn main() { + let bytes = vec![ + ]; + + println!("{}", sixel_string(&bytes, 128, 128, PixelFormat::RGB888, DiffusionMethod::Stucki, MethodForLargest::Auto, MethodForRep::Auto, Quality::AUTO).unwrap()); + }*/ diff --git a/src/quant.rs b/src/quant.rs index 2f13b75..e73d945 100644 --- a/src/quant.rs +++ b/src/quant.rs @@ -1,392 +1,268 @@ #![allow(clippy::erasing_op)] - /***************************************************************************** - * - * quantization - * - *****************************************************************************/ +/***************************************************************************** + * + * quantization + * + *****************************************************************************/ +use std::cmp::Ordering; use std::vec; +#[derive(Clone)] +struct bbox { + pub ind: i32, + pub colors: i32, + pub sum: i32, +} - /* - typedef struct box* boxVector; - struct box { - unsigned int ind; - unsigned int colors; - unsigned int sum; - }; - - typedef unsigned long sample; - typedef sample * tuple; - - struct tupleint { - /* An ordered pair of a tuple value and an integer, such as you - would find in a tuple table or tuple hash. - Note that this is a variable length structure. - */ - unsigned int value; - sample tuple[1]; - /* This is actually a variable size array -- its size is the - depth of the tuple in question. Some compilers do not let us - declare a variable length array. - */ - }; - typedef struct tupleint ** tupletable; - - typedef struct { - unsigned int size; - tupletable table; - } tupletable2; - - static unsigned int compareplanePlane; +/* +typedef struct box* boxVector; - */ - /* This is a parameter to compareplane(). We use this global variable - so that compareplane() can be called by qsort(), to compare two - tuples. qsort() doesn't pass any arguments except the two tuples. - */ -/* - static int - compareplane(const void * const arg1, - const void * const arg2) - { - int lhs, rhs; - - typedef const struct tupleint * const * const sortarg; - sortarg comparandPP = (sortarg) arg1; - sortarg comparatorPP = (sortarg) arg2; - lhs = (int)(*comparandPP)->tuple[compareplanePlane]; - rhs = (int)(*comparatorPP)->tuple[compareplanePlane]; - - return lhs - rhs; - } - - - static int - sumcompare(const void * const b1, const void * const b2) - { - return (int)((boxVector)b2)->sum - (int)((boxVector)b1)->sum; - } - - - static SIXELSTATUS - alloctupletable( - tupletable /* out */ *result, - unsigned int const /* in */ depth, - unsigned int const /* in */ size, - sixel_allocator_t /* in */ *allocator) - { - SIXELSTATUS status = SIXEL_FALSE; - enum { message_buffer_size = 256 }; - char message[message_buffer_size]; - int nwrite; - unsigned int mainTableSize; - unsigned int tupleIntSize; - unsigned int allocSize; - void * pool; - tupletable tbl; - unsigned int i; - - if (UINT_MAX / sizeof(struct tupleint) < size) { - nwrite = sprintf(message, - "size %u is too big for arithmetic", - size); - if (nwrite > 0) { - sixel_helper_set_additional_message(message); - } - status = SIXEL_RUNTIME_ERROR; - goto end; - } - - mainTableSize = size * sizeof(struct tupleint *); - tupleIntSize = sizeof(struct tupleint) - sizeof(sample) - + depth * sizeof(sample); - - /* To save the enormous amount of time it could take to allocate - each individual tuple, we do a trick here and allocate everything - as a single malloc block and suballocate internally. - */ - if ((UINT_MAX - mainTableSize) / tupleIntSize < size) { - nwrite = sprintf(message, - "size %u is too big for arithmetic", - size); - if (nwrite > 0) { - sixel_helper_set_additional_message(message); - } - status = SIXEL_RUNTIME_ERROR; - goto end; - } - - allocSize = mainTableSize + size * tupleIntSize; - - pool = sixel_allocator_malloc(allocator, allocSize); - if (pool == NULL) { - sprintf(message, - "unable to allocate %u bytes for a %u-entry " - "tuple table", - allocSize, size); - sixel_helper_set_additional_message(message); - status = SIXEL_BAD_ALLOCATION; - goto end; - } - tbl = (tupletable) pool; - - for (i = 0; i < size; ++i) - tbl[i] = (struct tupleint *) - ((char*)pool + mainTableSize + i * tupleIntSize); - - *result = tbl; - - status = SIXEL_OK; - - end: - return status; - } - - - /* +typedef unsigned long sample; +typedef sample * tuple; + +struct tupleint { + /* An ordered pair of a tuple value and an integer, such as you + would find in a tuple table or tuple hash. + Note that this is a variable length structure. + */ + unsigned int value; + sample tuple[1]; + /* This is actually a variable size array -- its size is the + depth of the tuple in question. Some compilers do not let us + declare a variable length array. + */ +}; +typedef struct tupleint ** tupletable; + +typedef struct { + unsigned int size; + tupletable table; +} tupletable2; + +static unsigned int compareplanePlane; + +*/ +/* This is a parameter to compareplane(). We use this global variable + so that compareplane() can be called by qsort(), to compare two + tuples. qsort() doesn't pass any arguments except the two tuples. +*/ +/* +static int +compareplane(const void * const arg1, + const void * const arg2) +{ + int lhs, rhs; + + typedef const struct tupleint * const * const sortarg; + sortarg comparandPP = (sortarg) arg1; + sortarg comparatorPP = (sortarg) arg2; + lhs = (int)(*comparandPP)->tuple[compareplanePlane]; + rhs = (int)(*comparatorPP)->tuple[compareplanePlane]; + + return lhs - rhs; +}*/ + +fn sumcompare(b1: &bbox, b2: &bbox) -> Ordering { + b2.sum.cmp(&b1.sum) +} + +/* ** Here is the fun part, the median-cut colormap generator. This is based ** on Paul Heckbert's paper "Color Image Quantization for Frame Buffer ** Display", SIGGRAPH '82 Proceedings, page 297. */ - - static tupletable2 - newColorMap(unsigned int const newcolors, unsigned int const depth, sixel_allocator_t *allocator) - { - SIXELSTATUS status = SIXEL_FALSE; - tupletable2 colormap; - unsigned int i; - - colormap.size = 0; - status = alloctupletable(&colormap.table, depth, newcolors, allocator); - if (SIXEL_FAILED(status)) { - goto end; - } - if (colormap.table) { - for (i = 0; i < newcolors; ++i) { - unsigned int plane; - for (plane = 0; plane < depth; ++plane) - colormap.table[i]->tuple[plane] = 0; - } - colormap.size = newcolors; - } - - end: - return colormap; - } - - - static boxVector - newBoxVector( - unsigned int const /* in */ colors, - unsigned int const /* in */ sum, - unsigned int const /* in */ newcolors, - sixel_allocator_t /* in */ *allocator) - { - boxVector bv; - - bv = (boxVector)sixel_allocator_malloc(allocator, - sizeof(struct box) * (size_t)newcolors); - if (bv == NULL) { - quant_trace(stderr, "out of memory allocating box vector table\n"); - return NULL; - } - - /* Set up the initial box. */ - bv[0].ind = 0; - bv[0].colors = colors; - bv[0].sum = sum; - - return bv; - } - - - static void - findBoxBoundaries(tupletable2 const colorfreqtable, - unsigned int const depth, - unsigned int const boxStart, - unsigned int const boxSize, - sample minval[], - sample maxval[]) - { - /*---------------------------------------------------------------------------- - Go through the box finding the minimum and maximum of each - component - the boundaries of the box. - -----------------------------------------------------------------------------*/ - unsigned int plane; - unsigned int i; - - for (plane = 0; plane < depth; ++plane) { - minval[plane] = colorfreqtable.table[boxStart]->tuple[plane]; - maxval[plane] = minval[plane]; - } - - for (i = 1; i < boxSize; ++i) { - for (plane = 0; plane < depth; ++plane) { - sample const v = colorfreqtable.table[boxStart + i]->tuple[plane]; - if (v < minval[plane]) minval[plane] = v; - if (v > maxval[plane]) maxval[plane] = v; - } - } - } - - - - static unsigned int - largestByNorm(sample minval[], sample maxval[], unsigned int const depth) - { - - unsigned int largestDimension; - unsigned int plane; - sample largestSpreadSoFar; - - largestSpreadSoFar = 0; - largestDimension = 0; - for (plane = 0; plane < depth; ++plane) { - sample const spread = maxval[plane]-minval[plane]; - if (spread > largestSpreadSoFar) { - largestDimension = plane; - largestSpreadSoFar = spread; - } - } - return largestDimension; - } - - - - static unsigned int - largestByLuminosity(sample minval[], sample maxval[], unsigned int const depth) - { - /*---------------------------------------------------------------------------- - This subroutine presumes that the tuple type is either - BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3). - To save time, we don't actually check it. - -----------------------------------------------------------------------------*/ - unsigned int retval; - - double lumin_factor[3] = {0.2989, 0.5866, 0.1145}; - - if (depth == 1) { - retval = 0; - } else { - /* An RGB tuple */ - unsigned int largestDimension; - unsigned int plane; - double largestSpreadSoFar; - - largestSpreadSoFar = 0.0; - largestDimension = 0; - - for (plane = 0; plane < 3; ++plane) { - double const spread = - lumin_factor[plane] * (maxval[plane]-minval[plane]); - if (spread > largestSpreadSoFar) { - largestDimension = plane; - largestSpreadSoFar = spread; - } - } - retval = largestDimension; - } - return retval; - } - - - - static void - centerBox(unsigned int const boxStart, - unsigned int const boxSize, - tupletable2 const colorfreqtable, - unsigned int const depth, - tuple const newTuple) - { - - unsigned int plane; - sample minval, maxval; - unsigned int i; - - for (plane = 0; plane < depth; ++plane) { - minval = maxval = colorfreqtable.table[boxStart]->tuple[plane]; - - for (i = 1; i < boxSize; ++i) { - sample v = colorfreqtable.table[boxStart + i]->tuple[plane]; - minval = minval < v ? minval: v; - maxval = maxval > v ? maxval: v; - } - newTuple[plane] = (minval + maxval) / 2; - } - } - - - - static void - averageColors(unsigned int const boxStart, - unsigned int const boxSize, - tupletable2 const colorfreqtable, - unsigned int const depth, - tuple const newTuple) - { - unsigned int plane; - sample sum; - unsigned int i; - - for (plane = 0; plane < depth; ++plane) { - sum = 0; - - for (i = 0; i < boxSize; ++i) { - sum += colorfreqtable.table[boxStart + i]->tuple[plane]; - } - - newTuple[plane] = sum / boxSize; - } - } - - - - static void - averagePixels(unsigned int const boxStart, - unsigned int const boxSize, - tupletable2 const colorfreqtable, - unsigned int const depth, - tuple const newTuple) - { - - unsigned int n; - /* Number of tuples represented by the box */ - unsigned int plane; - unsigned int i; - - /* Count the tuples in question */ - n = 0; /* initial value */ - for (i = 0; i < boxSize; ++i) { - n += (unsigned int)colorfreqtable.table[boxStart + i]->value; - } - - for (plane = 0; plane < depth; ++plane) { - sample sum; - - sum = 0; - - for (i = 0; i < boxSize; ++i) { - sum += colorfreqtable.table[boxStart + i]->tuple[plane] - * (unsigned int)colorfreqtable.table[boxStart + i]->value; - } - - newTuple[plane] = sum / n; - } - } - - - - static tupletable2 - colormapFromBv(unsigned int const newcolors, - boxVector const bv, - unsigned int const boxes, - tupletable2 const colorfreqtable, - unsigned int const depth, - int const methodForRep, - sixel_allocator_t *allocator) - { - /* + +pub fn newColorMap(newcolors: i32, depth: i32) -> HashMap { + let mut colormap = HashMap::new(); + for i in 0..newcolors as i32 { + colormap.insert( + i, + Tuple { + value: 0, + tuple: vec![0; depth as usize], + }, + ); + } + return colormap; +} + +fn newBoxVector(colors: i32, sum: i32, newcolors: i32) -> Vec { + let mut result = vec![ + bbox { + ind: 0, + colors: 0, + sum: 0 + }; + newcolors as usize + ]; + + /* Set up the initial box. */ + result[0].ind = 0; + result[0].colors = colors; + result[0].sum = sum; + + result +} + +pub fn findBoxBoundaries( + colorfreqtable: &mut HashMap, + depth: i32, + boxStart: i32, + boxSize: i32, + minval: &mut [i32], + maxval: &mut [i32], +) { + /*---------------------------------------------------------------------------- + Go through the box finding the minimum and maximum of each + component - the boundaries of the box. + -----------------------------------------------------------------------------*/ + + for plane in 0..depth { + minval[plane as usize] = + colorfreqtable.get(&(boxStart as i32)).unwrap().tuple[plane as usize]; + maxval[plane as usize] = minval[plane as usize]; + } + for i in 1..boxSize { + for plane in 0..depth { + let v = colorfreqtable + .get(&(boxStart as i32 + i as i32)) + .unwrap() + .tuple[plane as usize]; + minval[plane as usize] = minval[plane as usize].min(v); + maxval[plane as usize] = maxval[plane as usize].max(v); + } + } +} + +pub fn largestByNorm(minval: &[i32], maxval: &[i32], depth: i32) -> i32 { + let mut largestSpreadSoFar = 0; + let mut largestDimension = 0; + for plane in 0..depth as usize { + let spread = maxval[plane] - minval[plane]; + if (spread > largestSpreadSoFar) { + largestDimension = plane; + largestSpreadSoFar = spread; + } + } + largestDimension as i32 +} + +pub fn largestByLuminosity(minval: &[i32], maxval: &[i32], depth: i32) -> i32 { + /*---------------------------------------------------------------------------- + This subroutine presumes that the tuple type is either + BLACKANDWHITE, GRAYSCALE, or RGB (which implies pamP->depth is 1 or 3). + To save time, we don't actually check it. + -----------------------------------------------------------------------------*/ + let mut retval = 0; + + let lumin_factor = [0.2989, 0.5866, 0.1145]; + + if depth == 1 { + retval = 0; + } else { + /* An RGB tuple */ + let mut largestSpreadSoFar = 0.0; + let mut largestDimension = 0; + + for plane in 0..3 { + let spread = lumin_factor[plane] * (maxval[plane] - minval[plane]) as f32; + if spread > largestSpreadSoFar { + largestDimension = plane; + largestSpreadSoFar = spread; + } + } + retval = largestDimension; + } + retval as i32 +} + +pub fn centerBox( + boxStart: i32, + boxSize: i32, + colorfreqtable: &mut HashMap, + depth: i32, + newTuple: &mut Vec, +) { + for plane in 0..depth { + let mut maxval = colorfreqtable.get(&(boxStart as i32)).unwrap().tuple[plane as usize]; + let mut minval = maxval; + + for i in 1..boxSize { + let v = colorfreqtable + .get(&(boxStart as i32 + i as i32)) + .unwrap() + .tuple[plane as usize]; + minval = minval.min(v); + maxval = maxval.max(v); + } + newTuple[plane as usize] = (minval + maxval) / 2; + } +} + +pub fn averageColors( + boxStart: i32, + boxSize: i32, + colorfreqtable: &mut HashMap, + depth: i32, + newTuple: &mut Vec, +) { + for plane in 0..depth { + let mut sum = 0; + + for i in 0..boxSize { + sum += colorfreqtable + .get(&(boxStart as i32 + i as i32)) + .unwrap() + .tuple[plane as usize]; + } + + newTuple[plane as usize] = sum / boxSize; + } +} + +pub fn averagePixels( + boxStart: i32, + boxSize: i32, + colorfreqtable: &mut HashMap, + depth: i32, + newTuple: &mut Vec, +) { + /* Number of tuples represented by the box */ + /* Count the tuples in question */ + let mut n = 0; /* initial value */ + for i in 0..boxSize { + n += colorfreqtable + .get(&(boxStart as i32 + i as i32)) + .unwrap() + .value; + } + + for plane in 0..depth { + let mut sum = 0; + for i in 0..boxSize { + sum += colorfreqtable + .get(&(boxStart as i32 + i as i32)) + .unwrap() + .tuple[plane as usize] + * colorfreqtable + .get(&(boxStart as i32 + i as i32)) + .unwrap() + .value; + } + newTuple[plane as usize] = sum / n; + } +} + +fn colormapFromBv( + newcolors: i32, + bv: &Vec, + boxes: i32, + colorfreqtable: &mut HashMap, + depth: i32, + methodForRep: MethodForRep, +) -> HashMap { + /* ** Ok, we've got enough boxes. Now choose a representative color for ** each box. There are a number of possible ways to make this choice. ** One would be to choose the center of the box; this ignores any structure @@ -394,707 +270,711 @@ use std::vec; ** the box - this is the method specified in Heckbert's paper. A third ** method is to average all the pixels in the box. */ - tupletable2 colormap; - unsigned int bi; - - colormap = newColorMap(newcolors, depth, allocator); - if (!colormap.size) { - return colormap; - } - - for (bi = 0; bi < boxes; ++bi) { - switch (methodForRep) { - case SIXEL_REP_CENTER_BOX: - centerBox(bv[bi].ind, bv[bi].colors, - colorfreqtable, depth, - colormap.table[bi]->tuple); - break; - case SIXEL_REP_AVERAGE_COLORS: - averageColors(bv[bi].ind, bv[bi].colors, - colorfreqtable, depth, - colormap.table[bi]->tuple); - break; - case SIXEL_REP_AVERAGE_PIXELS: - averagePixels(bv[bi].ind, bv[bi].colors, - colorfreqtable, depth, - colormap.table[bi]->tuple); - break; - default: - quant_trace(stderr, "Internal error: " - "invalid value of methodForRep: %d\n", - methodForRep); - } - } - return colormap; - } - - - static SIXELSTATUS - splitBox(boxVector const bv, - unsigned int *const boxesP, - unsigned int const bi, - tupletable2 const colorfreqtable, - unsigned int const depth, - int const methodForLargest) - { - /*---------------------------------------------------------------------------- - Split Box 'bi' in the box vector bv (so that bv contains one more box - than it did as input). Split it so that each new box represents about - half of the pixels in the distribution given by 'colorfreqtable' for - the colors in the original box, but with distinct colors in each of the - two new boxes. - - Assume the box contains at least two colors. - -----------------------------------------------------------------------------*/ - SIXELSTATUS status = SIXEL_FALSE; - unsigned int const boxStart = bv[bi].ind; - unsigned int const boxSize = bv[bi].colors; - unsigned int const sm = bv[bi].sum; - - enum { max_depth= 16 }; - sample minval[max_depth]; - sample maxval[max_depth]; - - /* assert(max_depth >= depth); */ - - unsigned int largestDimension; - /* number of the plane with the largest spread */ - unsigned int medianIndex; - unsigned int lowersum; - /* Number of pixels whose value is "less than" the median */ - - findBoxBoundaries(colorfreqtable, depth, boxStart, boxSize, - minval, maxval); - - /* Find the largest dimension, and sort by that component. I have - included two methods for determining the "largest" dimension; - first by simply comparing the range in RGB space, and second by - transforming into luminosities before the comparison. - */ - switch (methodForLargest) { - case SIXEL_LARGE_NORM: - largestDimension = largestByNorm(minval, maxval, depth); - break; - case SIXEL_LARGE_LUM: - largestDimension = largestByLuminosity(minval, maxval, depth); - break; - default: - sixel_helper_set_additional_message( - "Internal error: invalid value of methodForLargest."); - status = SIXEL_LOGIC_ERROR; - goto end; - } - - /* TODO: I think this sort should go after creating a box, - not before splitting. Because you need the sort to use - the SIXEL_REP_CENTER_BOX method of choosing a color to - represent the final boxes - */ - - /* Set the gross global variable 'compareplanePlane' as a - parameter to compareplane(), which is called by qsort(). - */ - compareplanePlane = largestDimension; - qsort((char*) &colorfreqtable.table[boxStart], boxSize, - sizeof(colorfreqtable.table[boxStart]), - compareplane); - - { - /* Now find the median based on the counts, so that about half - the pixels (not colors, pixels) are in each subdivision. */ - - unsigned int i; - - lowersum = colorfreqtable.table[boxStart]->value; /* initial value */ - for (i = 1; i < boxSize - 1 && lowersum < sm / 2; ++i) { - lowersum += colorfreqtable.table[boxStart + i]->value; - } - medianIndex = i; - } - /* Split the box, and sort to bring the biggest boxes to the top. */ - - bv[bi].colors = medianIndex; - bv[bi].sum = lowersum; - bv[*boxesP].ind = boxStart + medianIndex; - bv[*boxesP].colors = boxSize - medianIndex; - bv[*boxesP].sum = sm - lowersum; - ++(*boxesP); - qsort((char*) bv, *boxesP, sizeof(struct box), sumcompare); - - status = SIXEL_OK; - - end: - return status; - } - - - - static SIXELSTATUS - mediancut(tupletable2 const colorfreqtable, - unsigned int const depth, - unsigned int const newcolors, - int const methodForLargest, - int const methodForRep, - tupletable2 *const colormapP, - sixel_allocator_t *allocator) - { - /*---------------------------------------------------------------------------- - Compute a set of only 'newcolors' colors that best represent an - image whose pixels are summarized by the histogram - 'colorfreqtable'. Each tuple in that table has depth 'depth'. - colorfreqtable.table[i] tells the number of pixels in the subject image - have a particular color. - - As a side effect, sort 'colorfreqtable'. - -----------------------------------------------------------------------------*/ - boxVector bv; - unsigned int bi; - unsigned int boxes; - int multicolorBoxesExist; - unsigned int i; - unsigned int sum; - SIXELSTATUS status = SIXEL_FALSE; - - sum = 0; - - for (i = 0; i < colorfreqtable.size; ++i) { - sum += colorfreqtable.table[i]->value; - } - - /* There is at least one box that contains at least 2 colors; ergo, - there is more splitting we can do. */ - bv = newBoxVector(colorfreqtable.size, sum, newcolors, allocator); - if (bv == NULL) { - goto end; - } - boxes = 1; - multicolorBoxesExist = (colorfreqtable.size > 1); - - /* Main loop: split boxes until we have enough. */ - while (boxes < newcolors && multicolorBoxesExist) { - /* Find the first splittable box. */ - for (bi = 0; bi < boxes && bv[bi].colors < 2; ++bi) - ; - if (bi >= boxes) { - multicolorBoxesExist = 0; - } else { - status = splitBox(bv, &boxes, bi, - colorfreqtable, depth, - methodForLargest); - if (SIXEL_FAILED(status)) { - goto end; - } - } - } - *colormapP = colormapFromBv(newcolors, bv, boxes, - colorfreqtable, depth, - methodForRep, allocator); - - sixel_allocator_free(allocator, bv); - - status = SIXEL_OK; - - end: - return status; - } - */ - - pub fn - computeHash(data: &[u8], i: usize, depth: i32) -> i32 - { + let mut colormap = newColorMap(newcolors, depth); + + for bi in 0..boxes { + match methodForRep { + MethodForRep::CenterBox => { + centerBox( + bv[bi as usize].ind, + bv[bi as usize].colors, + colorfreqtable, + depth, + &mut colormap.get_mut(&bi).unwrap().tuple, + ); + } + MethodForRep::AverageColors => { + averageColors( + bv[bi as usize].ind, + bv[bi as usize].colors, + colorfreqtable, + depth, + &mut colormap.get_mut(&bi).unwrap().tuple, + ); + } + MethodForRep::Auto | MethodForRep::Pixels => { + averagePixels( + bv[bi as usize].ind, + bv[bi as usize].colors, + colorfreqtable, + depth, + &mut colormap.get_mut(&bi).unwrap().tuple, + ); + } + } + } + colormap +} + +fn splitBox( + bv: &mut Vec, + boxesP: &mut i32, + bi: usize, + colorfreqtable: &mut HashMap, + depth: i32, + methodForLargest: MethodForLargest, +) -> SixelResult<()> { + /*---------------------------------------------------------------------------- + Split Box 'bi' in the box vector bv (so that bv contains one more box + than it did as input). Split it so that each new box represents about + half of the pixels in the distribution given by 'colorfreqtable' for + the colors in the original box, but with distinct colors in each of the + two new boxes. + + Assume the box contains at least two colors. + -----------------------------------------------------------------------------*/ + let boxStart = bv[bi].ind; + let boxSize = bv[bi].colors; + let sm = bv[bi].sum; + + let max_depth = 16; + let mut minval = vec![0; max_depth]; + let mut maxval = vec![0; max_depth]; + + /* assert(max_depth >= depth); */ + + findBoxBoundaries( + colorfreqtable, + depth, + boxStart, + boxSize, + &mut minval, + &mut maxval, + ); + + /* Find the largest dimension, and sort by that component. I have + included two methods for determining the "largest" dimension; + first by simply comparing the range in RGB space, and second by + transforming into luminosities before the comparison. + */ + let largestDimension = match methodForLargest { + MethodForLargest::Auto | MethodForLargest::Norm => largestByNorm(&minval, &maxval, depth), + MethodForLargest::Lum => largestByLuminosity(&minval, &maxval, depth), + }; + + /* TODO: I think this sort should go after creating a box, + not before splitting. Because you need the sort to use + the SIXEL_REP_CENTER_BOX method of choosing a color to + represent the final boxes + */ + + /* Set the gross global variable 'compareplanePlane' as a + parameter to compareplane(), which is called by qsort(). + */ + + /* Sholdn't be needed - I use a stupid hasmap - should be refactored. + compareplanePlane = largestDimension; + qsort((char*) &colorfreqtable.table[boxStart], boxSize, + sizeof(colorfreqtable.table[boxStart]), + compareplane);*/ + + /* Now find the median based on the counts, so that about half + the pixels (not colors, pixels) are in each subdivision. */ + let mut lowersum = colorfreqtable.get(&boxStart).unwrap().value; /* initial value */ + let mut i = 1; + while i < boxSize - 1 && lowersum < sm / 2 { + lowersum += colorfreqtable.get(&(boxStart + i)).unwrap().value; + i += 1; + } + let medianIndex = i; + /* Split the box, and sort to bring the biggest boxes to the top. */ + + bv[bi].colors = medianIndex; + bv[bi].sum = lowersum; + bv[*boxesP as usize].ind = boxStart + medianIndex; + bv[*boxesP as usize].colors = boxSize - medianIndex; + bv[*boxesP as usize].sum = sm - lowersum; + (*boxesP) += 1; + + bv[0..*boxesP as usize].sort_by(sumcompare); + Ok(()) +} + +pub fn mediancut( + colorfreqtable: &mut HashMap, + depth: i32, + newcolors: i32, + methodForLargest: MethodForLargest, + methodForRep: MethodForRep, + colormapP: &mut HashMap, +) -> SixelResult<()> { + /*---------------------------------------------------------------------------- + Compute a set of only 'newcolors' colors that best represent an + image whose pixels are summarized by the histogram + 'colorfreqtable'. Each tuple in that table has depth 'depth'. + colorfreqtable.table[i] tells the number of pixels in the subject image + have a particular color. + + As a side effect, sort 'colorfreqtable'. + -----------------------------------------------------------------------------*/ + let mut sum = 0; + + for i in 0..colorfreqtable.len() { + sum += colorfreqtable.get(&(i as i32)).unwrap().value; + } + + /* There is at least one box that contains at least 2 colors; ergo, + there is more splitting we can do. */ + let mut bv = newBoxVector(colorfreqtable.len() as i32, sum, newcolors); + let mut boxes = 1; + let mut multicolorBoxesExist = colorfreqtable.len() > 1; + + /* Main loop: split boxes until we have enough. */ + while (boxes < newcolors && multicolorBoxesExist) { + /* Find the first splittable box. */ + let mut bi = 0; + while bi < boxes && bv[bi as usize].colors < 2 { + bi += 1; + } + + if bi >= boxes { + multicolorBoxesExist = false; + } else { + splitBox( + &mut bv, + &mut boxes, + bi as usize, + colorfreqtable, + depth, + methodForLargest, + )?; + } + } + *colormapP = colormapFromBv( + newcolors, + &mut bv, + boxes, + colorfreqtable, + depth, + methodForRep, + ); + + Ok(()) +} + +pub fn computeHash(data: &[u8], i: usize, depth: i32) -> i32 { let mut hash = 0; for n in 0..depth { hash |= (data[i + depth as usize - 1 - n as usize] as i32 >> 3) << (n * 5); } hash - } +} #[derive(Clone)] pub struct Tuple { pub value: i32, pub tuple: Vec, - } - - pub fn - computeHistogram(data: &[u8], +} + +pub fn computeHistogram( + data: &[u8], length: i32, depth: i32, - qualityMode: Quality) ->SixelResult> - { + qualityMode: Quality, +) -> SixelResult> { let (max_sample, mut step) = match qualityMode { Quality::LOW => (18383, length / depth / 18383 * depth), Quality::HIGH => (18383, length / depth / 18383 * depth), - Quality::AUTO | - Quality::HIGHCOLOR | - Quality::FULL => (4003079, length / depth / 4003079 * depth), + Quality::AUTO | Quality::HIGHCOLOR | Quality::FULL => { + (4003079, length / depth / 4003079 * depth) + } }; - if length < max_sample * depth { - step = 6 * depth; - } - - if step <= 0 { - step = depth; - } - - let mut histogram = vec![0; 1 << (depth * 5)]; - - let mut memory = vec![0; 1 << (depth * 5)]; - let mut it = 0; - let mut refe = 0; - let mut refmap = 0; - - let mut i = 0; - while i < length { - let bucket_index = computeHash(data, i as usize, 3) as usize; - if histogram[bucket_index] == 0 { - memory[refe] = bucket_index; - refe+=1; - } - if histogram[bucket_index] < (1 << (2 * 8)) - 1 { - histogram[bucket_index] += 1; - } - - i += step; - } - let mut colorfreqtable = HashMap::new(); - - for i in 0..refe { - if histogram[memory[i]] > 0 { + if length < max_sample * depth { + step = 6 * depth; + } + + if step <= 0 { + step = depth; + } + + let mut histogram = vec![0; 1 << (depth * 5)]; + + let mut memory = vec![0; 1 << (depth * 5)]; + let mut it = 0; + let mut refe = 0; + let mut refmap = 0; + + let mut i = 0; + while i < length { + let bucket_index = computeHash(data, i as usize, 3) as usize; + if histogram[bucket_index] == 0 { + memory[refe] = bucket_index; + refe += 1; + } + if histogram[bucket_index] < (1 << (2 * 8)) - 1 { + histogram[bucket_index] += 1; + } + + i += step; + } + let mut colorfreqtable = HashMap::new(); + + for i in 0..refe { + if histogram[memory[i]] > 0 { let mut tuple: Vec = vec![0; depth as usize]; for n in 0..depth { - tuple[(depth - 1 - n) as usize] - = ((memory[it] >> (n * 5) & 0x1f) << 3) as i32; - } - colorfreqtable.insert(i as i32,Tuple { - value: histogram[memory[i]], - tuple - }); - } - it += 1; - } - Ok(colorfreqtable) - } - - - pub fn - computeColorMapFromInput(data: &[u8], - length:i32, - depth:i32, - reqColors:i32, - methodForLargest:FindLargestDim, - methodForRep:ColorChoosingMethod, - qualityMode:Quality, - colormapP: &mut HashMap, - origcolors: &mut i32) ->SixelResult<()> - { - /*---------------------------------------------------------------------------- - Produce a colormap containing the best colors to represent the - image stream in file 'ifP'. Figure it out using the median cut - technique. - - The colormap will have 'reqcolors' or fewer colors in it, unless - 'allcolors' is true, in which case it will have all the colors that - are in the input. - - The colormap has the same maxval as the input. - - Put the colormap in newly allocated storage as a tupletable2 - and return its address as *colormapP. Return the number of colors in - it as *colorsP and its maxval as *colormapMaxvalP. - - Return the characteristics of the input file as - *formatP and *freqPamP. (This information is not really - relevant to our colormap mission; just a fringe benefit). - -----------------------------------------------------------------------------*/ - + tuple[(depth - 1 - n) as usize] = ((memory[it] >> (n * 5) & 0x1f) << 3) as i32; + } + colorfreqtable.insert( + i as i32, + Tuple { + value: histogram[memory[i]], + tuple, + }, + ); + } + it += 1; + } + Ok(colorfreqtable) +} + +pub fn computeColorMapFromInput( + data: &[u8], + length: i32, + depth: i32, + reqColors: i32, + methodForLargest: MethodForLargest, + methodForRep: MethodForRep, + qualityMode: Quality, + colormapP: &mut HashMap, + origcolors: &mut i32, +) -> SixelResult<()> { + /*---------------------------------------------------------------------------- + Produce a colormap containing the best colors to represent the + image stream in file 'ifP'. Figure it out using the median cut + technique. + + The colormap will have 'reqcolors' or fewer colors in it, unless + 'allcolors' is true, in which case it will have all the colors that + are in the input. + + The colormap has the same maxval as the input. + + Put the colormap in newly allocated storage as a tupletable2 + and return its address as *colormapP. Return the number of colors in + it as *colorsP and its maxval as *colormapMaxvalP. + + Return the characteristics of the input file as + *formatP and *freqPamP. (This information is not really + relevant to our colormap mission; just a fringe benefit). + -----------------------------------------------------------------------------*/ + let mut colorfreqtable = computeHistogram(data, length, depth, qualityMode)?; *origcolors = colorfreqtable.len() as i32; - - if colorfreqtable.len() as i32 <= reqColors { + + if colorfreqtable.len() as i32 <= reqColors { for i in colorfreqtable.len() as i32..=reqColors { let mut tuple: Vec = vec![0; depth as usize]; for n in 0..depth { tuple[n as usize] = (i * depth) + n; - } - colorfreqtable.insert(i, Tuple { - value: i, - tuple - }); + } + colorfreqtable.insert(i, Tuple { value: i, tuple }); } - + for i in 0..colorfreqtable.len() as i32 { colormapP.insert(i, colorfreqtable.get(&i).unwrap().clone()); - } - } else { - todo!("mediancut"); - /*/ - status = mediancut(colorfreqtable, depth, reqColors, - methodForLargest, methodForRep, colormapP, allocator); - if (SIXEL_FAILED(status)) { - goto end; - }*/ - } - Ok(()) - } - - /* diffuse error energy to surround pixels */ - pub fn - error_diffuse(data:&mut [u8], /* base address of pixel buffer */ - pos: i32, /* address of the destination pixel */ - depth: i32, /* color depth in bytes */ - error: i32, /* error energy */ - numerator: i32, /* numerator of diffusion coefficient */ - denominator: i32 /* denominator of diffusion coefficient */) - { - let offset= (pos * depth) as usize; - - let mut c = data[offset] as i32 + error * numerator / denominator; - if c < 0 { - c = 0; - } - if c >= 1 << 8 { - c = (1 << 8) - 1; - } - data[offset] = c as u8; - } - - - pub fn - diffuse_none(data:&mut [u8], width:i32, height:i32, - x:i32, y:i32, depth:i32, error:i32) - { - - } - - - pub fn - diffuse_fs(data:&mut [u8], width:i32, height:i32, - x:i32, y:i32, depth:i32, error:i32) - { - let pos = y * width + x; - - /* Floyd Steinberg Method - * curr 7/16 - * 3/16 5/48 1/16 - */ - if x < width - 1 && y < height - 1 { - /* add error to the right cell */ - error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 16); - /* add error to the left-bottom cell */ - error_diffuse(data, pos + width * 1 - 1, depth, error, 3, 16); - /* add error to the bottom cell */ - error_diffuse(data, pos + width * 1 + 0, depth, error, 5, 16); - /* add error to the right-bottom cell */ - error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 16); - } - } - - - pub fn - diffuse_atkinson(data:&mut [u8], width:i32, height:i32, - x:i32, y:i32, depth:i32, error:i32) - { - let pos = y * width + x; - - /* Atkinson's Method - * curr 1/8 1/8 - * 1/8 1/8 1/8 - * 1/8 - */ - if y < height - 2 { - /* add error to the right cell */ - error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 8); - /* add error to the 2th right cell */ - error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8); - /* add error to the left-bottom cell */ - error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8); - /* add error to the bottom cell */ - error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 8); - /* add error to the right-bottom cell */ - error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8); - /* add error to the 2th bottom cell */ - error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 8); - } - } - - - pub fn - diffuse_jajuni(data:&mut [u8], width:i32, height:i32, - x:i32, y:i32, depth:i32, error:i32) - { - let pos = y * width + x; - - /* Jarvis, Judice & Ninke Method - * curr 7/48 5/48 - * 3/48 5/48 7/48 5/48 3/48 - * 1/48 3/48 5/48 3/48 1/48 - */ - if pos < (height - 2) * width - 2 { - error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 48); - error_diffuse(data, pos + width * 0 + 2, depth, error, 5, 48); - error_diffuse(data, pos + width * 1 - 2, depth, error, 3, 48); - error_diffuse(data, pos + width * 1 - 1, depth, error, 5, 48); - error_diffuse(data, pos + width * 1 + 0, depth, error, 7, 48); - error_diffuse(data, pos + width * 1 + 1, depth, error, 5, 48); - error_diffuse(data, pos + width * 1 + 2, depth, error, 3, 48); - error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48); - error_diffuse(data, pos + width * 2 - 1, depth, error, 3, 48); - error_diffuse(data, pos + width * 2 + 0, depth, error, 5, 48); - error_diffuse(data, pos + width * 2 + 1, depth, error, 3, 48); - error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48); - } - } - - - pub fn - diffuse_stucki(data:&mut [u8], width:i32, height:i32, - x:i32, y:i32, depth:i32, error:i32) - { - let pos = y * width + x; - - /* Stucki's Method - * curr 8/48 4/48 - * 2/48 4/48 8/48 4/48 2/48 - * 1/48 2/48 4/48 2/48 1/48 - */ - if pos < (height - 2) * width - 2 { - error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 6); - error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 12); - error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 24); - error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 12); - error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 6); - error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 12); - error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 24); - error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48); - error_diffuse(data, pos + width * 2 - 1, depth, error, 1, 24); - error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 12); - error_diffuse(data, pos + width * 2 + 1, depth, error, 1, 24); - error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48); - } - } - - - pub fn - diffuse_burkes(data:&mut [u8], width:i32, height:i32, - x:i32, y:i32, depth:i32, error:i32) - { - let pos = y * width + x; - - /* Burkes' Method - * curr 4/16 2/16 - * 1/16 2/16 4/16 2/16 1/16 - */ - if pos < (height - 1) * width - 2 { - error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 4); - error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8); - error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 16); - error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8); - error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 4); - error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8); - error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 16); - } - } - - pub fn - mask_a (x:i32, y:i32, c:i32) -> f32 - { - return ((((x + c * 67) + y * 236) * 119) & 255 ) as f32 / 128.0 - 1.0; - } - - pub fn - mask_x (x:i32, y:i32, c:i32) -> f32 - { - return ((((x + c * 29) ^ y * 149) * 1234) & 511 ) as f32 / 256.0 - 1.0; - } + } + } else { + mediancut( + &mut colorfreqtable, + depth, + reqColors, + methodForLargest, + methodForRep, + colormapP, + )?; + } + Ok(()) +} -use std::{collections::HashMap, hash::Hash}; +/* diffuse error energy to surround pixels */ +pub fn error_diffuse( + data: &mut [u8], /* base address of pixel buffer */ + pos: i32, /* address of the destination pixel */ + depth: i32, /* color depth in bytes */ + error: i32, /* error energy */ + numerator: i32, /* numerator of diffusion coefficient */ + denominator: i32, /* denominator of diffusion coefficient */ +) { + let offset = (pos * depth) as usize; + + let mut c = data[offset] as i32 + error * numerator / denominator; + if c < 0 { + c = 0; + } + if c >= 1 << 8 { + c = (1 << 8) - 1; + } + data[offset] = c as u8; +} + +pub fn diffuse_none( + data: &mut [u8], + width: i32, + height: i32, + x: i32, + y: i32, + depth: i32, + error: i32, +) { +} + +pub fn diffuse_fs( + data: &mut [u8], + width: i32, + height: i32, + x: i32, + y: i32, + depth: i32, + error: i32, +) { + let pos = y * width + x; + + /* Floyd Steinberg Method + * curr 7/16 + * 3/16 5/48 1/16 + */ + if x < width - 1 && y < height - 1 { + /* add error to the right cell */ + error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 16); + /* add error to the left-bottom cell */ + error_diffuse(data, pos + width * 1 - 1, depth, error, 3, 16); + /* add error to the bottom cell */ + error_diffuse(data, pos + width * 1 + 0, depth, error, 5, 16); + /* add error to the right-bottom cell */ + error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 16); + } +} + +pub fn diffuse_atkinson( + data: &mut [u8], + width: i32, + height: i32, + x: i32, + y: i32, + depth: i32, + error: i32, +) { + let pos = y * width + x; + + /* Atkinson's Method + * curr 1/8 1/8 + * 1/8 1/8 1/8 + * 1/8 + */ + if y < height - 2 { + /* add error to the right cell */ + error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 8); + /* add error to the 2th right cell */ + error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8); + /* add error to the left-bottom cell */ + error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8); + /* add error to the bottom cell */ + error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 8); + /* add error to the right-bottom cell */ + error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8); + /* add error to the 2th bottom cell */ + error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 8); + } +} -use crate::{ColorChoosingMethod, SixelError, DiffusionMethod}; -use crate::{SixelResult, pixelformat::sixel_helper_compute_depth, FindLargestDim, ResampleMethod, PixelFormat, Quality}; +pub fn diffuse_jajuni( + data: &mut [u8], + width: i32, + height: i32, + x: i32, + y: i32, + depth: i32, + error: i32, +) { + let pos = y * width + x; + + /* Jarvis, Judice & Ninke Method + * curr 7/48 5/48 + * 3/48 5/48 7/48 5/48 3/48 + * 1/48 3/48 5/48 3/48 1/48 + */ + if pos < (height - 2) * width - 2 { + error_diffuse(data, pos + width * 0 + 1, depth, error, 7, 48); + error_diffuse(data, pos + width * 0 + 2, depth, error, 5, 48); + error_diffuse(data, pos + width * 1 - 2, depth, error, 3, 48); + error_diffuse(data, pos + width * 1 - 1, depth, error, 5, 48); + error_diffuse(data, pos + width * 1 + 0, depth, error, 7, 48); + error_diffuse(data, pos + width * 1 + 1, depth, error, 5, 48); + error_diffuse(data, pos + width * 1 + 2, depth, error, 3, 48); + error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48); + error_diffuse(data, pos + width * 2 - 1, depth, error, 3, 48); + error_diffuse(data, pos + width * 2 + 0, depth, error, 5, 48); + error_diffuse(data, pos + width * 2 + 1, depth, error, 3, 48); + error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48); + } +} + +pub fn diffuse_stucki( + data: &mut [u8], + width: i32, + height: i32, + x: i32, + y: i32, + depth: i32, + error: i32, +) { + let pos = y * width + x; + + /* Stucki's Method + * curr 8/48 4/48 + * 2/48 4/48 8/48 4/48 2/48 + * 1/48 2/48 4/48 2/48 1/48 + */ + if pos < (height - 2) * width - 2 { + error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 6); + error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 12); + error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 24); + error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 12); + error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 6); + error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 12); + error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 24); + error_diffuse(data, pos + width * 2 - 2, depth, error, 1, 48); + error_diffuse(data, pos + width * 2 - 1, depth, error, 1, 24); + error_diffuse(data, pos + width * 2 + 0, depth, error, 1, 12); + error_diffuse(data, pos + width * 2 + 1, depth, error, 1, 24); + error_diffuse(data, pos + width * 2 + 2, depth, error, 1, 48); + } +} +pub fn diffuse_burkes( + data: &mut [u8], + width: i32, + height: i32, + x: i32, + y: i32, + depth: i32, + error: i32, +) { + let pos = y * width + x; - /* lookup closest color from palette with "normal" strategy */ -pub fn - lookup_normal(pixel: &[u8], + /* Burkes' Method + * curr 4/16 2/16 + * 1/16 2/16 4/16 2/16 1/16 + */ + if pos < (height - 1) * width - 2 { + error_diffuse(data, pos + width * 0 + 1, depth, error, 1, 4); + error_diffuse(data, pos + width * 0 + 2, depth, error, 1, 8); + error_diffuse(data, pos + width * 1 - 2, depth, error, 1, 16); + error_diffuse(data, pos + width * 1 - 1, depth, error, 1, 8); + error_diffuse(data, pos + width * 1 + 0, depth, error, 1, 4); + error_diffuse(data, pos + width * 1 + 1, depth, error, 1, 8); + error_diffuse(data, pos + width * 1 + 2, depth, error, 1, 16); + } +} + +pub fn mask_a(x: i32, y: i32, c: i32) -> f32 { + return ((((x + c * 67) + y * 236) * 119) & 255) as f32 / 128.0 - 1.0; +} + +pub fn mask_x(x: i32, y: i32, c: i32) -> f32 { + return ((((x + c * 29) ^ y * 149) * 1234) & 511) as f32 / 256.0 - 1.0; +} + +use std::{collections::HashMap, hash::Hash}; + +use crate::{ + pixelformat::sixel_helper_compute_depth, MethodForLargest, PixelFormat, Quality, + ResampleMethod, SixelResult, +}; +use crate::{DiffusionMethod, MethodForRep, SixelError}; + +/* lookup closest color from palette with "normal" strategy */ +pub fn lookup_normal( + pixel: &[u8], depth: i32, palette: &[u8], reqcolor: i32, - cachetable: &mut Vec, - complexion: i32) -> i32 - { - - let mut result = -1; - let mut diff = i32::MAX; - - /* don't use cachetable in 'normal' strategy */ - - for i in 0..reqcolor { - let mut distant = 0; - let mut r = pixel[0] as i32 - palette[(i * depth + 0) as usize] as i32; - distant += r * r * complexion; - for n in 1..depth { - r = pixel[n as usize] as i32 - palette[(i * depth + n) as usize] as i32; - distant += r * r; - } - if distant < diff { - diff = distant; - result = i; - } - } - - return result; - } - - /* lookup closest color from palette with "fast" strategy */ - pub fn - lookup_fast(pixel: &[u8], + cachetable: &mut Vec, + complexion: i32, +) -> i32 { + let mut result = -1; + let mut diff = i32::MAX; + + /* don't use cachetable in 'normal' strategy */ + + for i in 0..reqcolor { + let mut distant = 0; + let mut r = pixel[0] as i32 - palette[(i * depth + 0) as usize] as i32; + distant += r * r * complexion; + for n in 1..depth { + r = pixel[n as usize] as i32 - palette[(i * depth + n) as usize] as i32; + distant += r * r; + } + if distant < diff { + diff = distant; + result = i; + } + } + + return result; +} + +/* lookup closest color from palette with "fast" strategy */ +pub fn lookup_fast( + pixel: &[u8], depth: i32, palette: &[u8], reqcolor: i32, - cachetable: &mut Vec, - complexion: i32) -> i32 - { + cachetable: &mut Vec, + complexion: i32, +) -> i32 { let mut result: i32 = -1; let mut diff = i32::MAX; let mut hash = computeHash(pixel, 0, 3); - + let cache = cachetable[hash as usize]; - if cache != 0 { /* fast lookup */ - return cache as i32 - 1; - } - /* collision */ - for i in 0..reqcolor { -/* distant = 0; - #if 0 - for (n = 0; n < 3; ++n) { - r = pixel[n] - palette[i * 3 + n]; - distant += r * r; - } - #elif 1*/ /* complexion correction */ - let i = i as usize; - let distant = - (pixel[0] as i32 - palette[i * 3 + 0] as i32) * (pixel[0] as i32 - palette[i * 3 + 0] as i32) * complexion - + (pixel[1] as i32 - palette[i * 3 + 1] as i32) * (pixel[1] as i32 - palette[i * 3 + 1] as i32) - + (pixel[2] as i32 - palette[i * 3 + 2] as i32) * (pixel[2] as i32 - palette[i * 3 + 2] as i32) - ; -// #endif - if distant < diff { - diff = distant; - result = i as i32; - } - } - cachetable[hash as usize] = (result + 1) as u16; - + if cache != 0 { + /* fast lookup */ + return cache as i32 - 1; + } + /* collision */ + for i in 0..reqcolor { + /* distant = 0; + #if 0 + for (n = 0; n < 3; ++n) { + r = pixel[n] - palette[i * 3 + n]; + distant += r * r; + } + #elif 1*/ + /* complexion correction */ + let i = i as usize; + let distant = (pixel[0] as i32 - palette[i * 3 + 0] as i32) + * (pixel[0] as i32 - palette[i * 3 + 0] as i32) + * complexion + + (pixel[1] as i32 - palette[i * 3 + 1] as i32) + * (pixel[1] as i32 - palette[i * 3 + 1] as i32) + + (pixel[2] as i32 - palette[i * 3 + 2] as i32) + * (pixel[2] as i32 - palette[i * 3 + 2] as i32); + // #endif + if distant < diff { + diff = distant; + result = i as i32; + } + } + cachetable[hash as usize] = (result + 1) as u16; + result - } +} - - pub fn - lookup_mono_darkbg(pixel: &[u8], +pub fn lookup_mono_darkbg( + pixel: &[u8], depth: i32, palette: &[u8], reqcolor: i32, - cachetable: &mut Vec, - complexion: i32) -> i32 - { + cachetable: &mut Vec, + complexion: i32, +) -> i32 { let mut distant = 0; for n in 0..depth { distant += pixel[n as usize] as i32; } - if distant >= 128 * reqcolor { 1 }else { 0} + if distant >= 128 * reqcolor { + 1 + } else { + 0 + } } - - pub fn - lookup_mono_lightbg(pixel: &[u8], + +pub fn lookup_mono_lightbg( + pixel: &[u8], depth: i32, palette: &[u8], reqcolor: i32, - cachetable: &mut Vec, - complexion: i32) -> i32 - { + cachetable: &mut Vec, + complexion: i32, +) -> i32 { let mut distant = 0; for n in 0..depth { distant += pixel[n as usize] as i32; } - if distant < 128 * reqcolor { 1 }else { 0} + if distant < 128 * reqcolor { + 1 + } else { + 0 + } } - - - /* choose colors using median-cut method */ - pub fn - sixel_quant_make_palette( - data: &[u8] , - length: i32, - pixelformat: PixelFormat, - reqcolors: i32, - ncolors: &mut i32, - origcolors: &mut i32, - methodForLargest: FindLargestDim, - methodForRep: ColorChoosingMethod, - qualityMode: Quality) -> SixelResult> - { - let result_depth = sixel_helper_compute_depth(pixelformat); - /*if (result_depth <= 0) { - *result = NULL; - goto end; - }*/ - - let depth = result_depth as usize; - let mut colormap = HashMap::new(); + +/* choose colors using median-cut method */ +pub fn sixel_quant_make_palette( + data: &[u8], + length: i32, + pixelformat: PixelFormat, + reqcolors: i32, + ncolors: &mut i32, + origcolors: &mut i32, + methodForLargest: MethodForLargest, + methodForRep: MethodForRep, + qualityMode: Quality, +) -> SixelResult> { + let result_depth = sixel_helper_compute_depth(pixelformat); + /*if (result_depth <= 0) { + *result = NULL; + goto end; + }*/ + + let depth = result_depth as usize; + let mut colormap = HashMap::new(); computeColorMapFromInput( - data, length, depth as i32, - reqcolors, methodForLargest, - methodForRep, qualityMode, - &mut colormap, origcolors); - *ncolors = *origcolors; - let mut result = vec![0; colormap.len() * depth as usize]; - for i in 0..colormap.len() { + data, + length, + depth as i32, + reqcolors, + methodForLargest, + methodForRep, + qualityMode, + &mut colormap, + origcolors, + ); + *ncolors = colormap.len() as i32; + let mut result = vec![0; colormap.len() * depth as usize]; + for i in 0..colormap.len() { for n in 0..depth { - result[i * depth + n] = colormap.get(&(i as i32)).unwrap().tuple[n] as u8; - } - } - Ok(result) - } - - + result[i * depth + n] = colormap.get(&(i as i32)).unwrap().tuple[n] as u8; + } + } + Ok(result) +} - /* apply color palette into specified pixel buffers */ - pub fn - sixel_quant_apply_palette( +/* apply color palette into specified pixel buffers */ +pub fn sixel_quant_apply_palette( result: &mut [u8], data: &mut [u8], width: i32, height: i32, depth: i32, palette: &mut Vec, - reqcolor:i32, + reqcolor: i32, methodForDiffuse: DiffusionMethod, foptimize: bool, foptimize_palette: bool, complexion: i32, - cachetable: Option<&mut Vec>) -> SixelResult - { + cachetable: Option<&mut Vec>, +) -> SixelResult { let mut ncolors: i32 = 0; - /* check bad reqcolor */ - if reqcolor < 1 { + /* check bad reqcolor */ + if reqcolor < 1 { /* - sixel_helper_set_additional_message( - "sixel_quant_apply_palette: " - "a bad argument is detected, reqcolor < 0."); - */ + sixel_helper_set_additional_message( + "sixel_quant_apply_palette: " + "a bad argument is detected, reqcolor < 0."); + */ return Err(Box::new(SixelError::BadArgument)); - } + } let mut f_mask = false; - - let f_diffuse = if depth != 3 { + let f_diffuse = if depth != 3 { diffuse_none - } else { - match methodForDiffuse { - DiffusionMethod::Auto | - DiffusionMethod::None => diffuse_none, + } else { + match methodForDiffuse { + DiffusionMethod::Auto | DiffusionMethod::None => diffuse_none, DiffusionMethod::Atkinson => diffuse_atkinson, DiffusionMethod::FS => diffuse_fs, DiffusionMethod::JaJuNi => diffuse_jajuni, @@ -1106,153 +986,179 @@ pub fn } DiffusionMethod::XDither => { f_mask = true; - diffuse_none + diffuse_none } } - }; - - let mut f_lookup: Option, i32) -> i32> = None; - if reqcolor == 2 { - let mut sum1 = 0; - let mut sum2 = 0; - for n in 0..depth { - sum1 += palette[n as usize] as i32; - } - for n in depth..(depth + depth) { - sum2 += palette[n as usize] as i32; - } - if (sum1 == 0 && sum2 == 255 * 3) { - f_lookup = Some(lookup_mono_darkbg); - } else if (sum1 == 255 * 3 && sum2 == 0) { - f_lookup = Some(lookup_mono_lightbg); - } - } - if f_lookup.is_none() { - if (foptimize && depth == 3) { - f_lookup = Some(lookup_fast); - } else { - f_lookup = Some(lookup_normal); - } - } - - let mut cc = vec![0u16, 1 << (depth * 5)]; - let mut indextable = match cachetable { - Some(table) => table, - None => &mut cc, - }; - - if foptimize_palette { - ncolors = 0; - - let mut new_palette = Vec::new(); - let mut migration_map = Vec::new(); - - if f_mask { + }; + + let mut f_lookup: Option, i32) -> i32> = None; + if reqcolor == 2 { + let mut sum1 = 0; + let mut sum2 = 0; + for n in 0..depth { + sum1 += palette[n as usize] as i32; + } + for n in depth..(depth + depth) { + sum2 += palette[n as usize] as i32; + } + if (sum1 == 0 && sum2 == 255 * 3) { + f_lookup = Some(lookup_mono_darkbg); + } else if (sum1 == 255 * 3 && sum2 == 0) { + f_lookup = Some(lookup_mono_lightbg); + } + } + if f_lookup.is_none() { + if (foptimize && depth == 3) { + f_lookup = Some(lookup_fast); + } else { + f_lookup = Some(lookup_normal); + } + } + + let mut cc = vec![0u16, 1 << (depth * 5)]; + let mut indextable = match cachetable { + Some(table) => table, + None => &mut cc, + }; + + if foptimize_palette { + ncolors = 0; + + let mut new_palette = Vec::new(); + let mut migration_map = Vec::new(); + + if f_mask { for y in 0..height { for x in 0..width { let mut copy: Vec = Vec::new(); - + let pos = y * width + x; - for d in 0..depth { - let mut val = data[(pos * depth + d) as usize] as i32; - if matches!(methodForDiffuse, DiffusionMethod::ADither) { + for d in 0..depth { + let mut val = data[(pos * depth + d) as usize] as i32; + if matches!(methodForDiffuse, DiffusionMethod::ADither) { val += (mask_a(x, y, d) * 32.0) as i32; } else { - val += (mask_x(x, y, d) * 32.0) as i32; + val += (mask_x(x, y, d) * 32.0) as i32; } - copy.push (val.clamp(0, 255) as u8); - } - // &[u8], i32, &[u8], i32, &mut Vec, i32 - let color_index = f_lookup.unwrap()(©, + copy.push(val.clamp(0, 255) as u8); + } + // &[u8], i32, &[u8], i32, &mut Vec, i32 + let color_index = f_lookup.unwrap()( + ©, depth, - &palette, reqcolor, &mut indextable, complexion) as usize; - if migration_map[color_index] == 0 { - result[pos as usize] = ncolors as u8; - for n in 0..depth { + &palette, + reqcolor, + &mut indextable, + complexion, + ) as usize; + if migration_map[color_index] == 0 { + result[pos as usize] = ncolors as u8; + for n in 0..depth { new_palette.push(palette[color_index * depth as usize + n as usize]); - } - ncolors += 1; - migration_map[color_index] = ncolors; - } else { - result[pos as usize] = migration_map[color_index] as u8 - 1; - } - } - } - *palette = new_palette; - } else { + } + ncolors += 1; + migration_map[color_index] = ncolors; + } else { + result[pos as usize] = migration_map[color_index] as u8 - 1; + } + } + } + *palette = new_palette; + } else { for y in 0..height { for x in 0..width { let pos = y * width + x; - let color_index = f_lookup.unwrap()(&data[(pos * depth) as usize..], depth, - palette, reqcolor, &mut indextable, complexion) as usize; - if (migration_map[color_index] == 0) { - result[pos as usize] = ncolors as u8; - for n in 0..depth { - new_palette[(ncolors * depth + n) as usize] = palette[(color_index * depth as usize + n as usize) as usize]; - } - ncolors += 1; - migration_map[color_index] = ncolors; - } else { - result[pos as usize] = migration_map[color_index] as u8 - 1; - } - for n in 0..depth { - let offset = data[(pos * depth + n)as usize] as i32 - palette[color_index * depth as usize + n as usize] as i32; + let color_index = f_lookup.unwrap()( + &data[(pos * depth) as usize..], + depth, + palette, + reqcolor, + &mut indextable, + complexion, + ) as usize; + if (migration_map[color_index] == 0) { + result[pos as usize] = ncolors as u8; + for n in 0..depth { + new_palette[(ncolors * depth + n) as usize] = + palette[(color_index * depth as usize + n as usize) as usize]; + } + ncolors += 1; + migration_map[color_index] = ncolors; + } else { + result[pos as usize] = migration_map[color_index] as u8 - 1; + } + for n in 0..depth { + let offset = data[(pos * depth + n) as usize] as i32 + - palette[color_index * depth as usize + n as usize] as i32; f_diffuse(&mut data[n as usize..], width, height, x, y, depth, offset); - } - } - } - *palette = new_palette; - } - } else { - if (f_mask) { + } + } + } + *palette = new_palette; + } + } else { + if (f_mask) { for y in 0..height { for x in 0..width { let mut copy: Vec = Vec::new(); - let pos = y * width + x; - for d in 0..depth { + let pos = y * width + x; + for d in 0..depth { let mut val = data[(pos * depth + d) as usize] as i32; if matches!(methodForDiffuse, DiffusionMethod::ADither) { val += (mask_a(x, y, d) * 32.0) as i32; } else { - val += (mask_x(x, y, d) * 32.0) as i32; + val += (mask_x(x, y, d) * 32.0) as i32; } - copy.push(val.clamp(0, 255) as u8); - } - result[pos as usize] = f_lookup.unwrap()(&mut copy, depth, - palette, reqcolor, &mut indextable, complexion) as u8; - } - } - } else { + copy.push(val.clamp(0, 255) as u8); + } + result[pos as usize] = f_lookup.unwrap()( + &mut copy, + depth, + palette, + reqcolor, + &mut indextable, + complexion, + ) as u8; + } + } + } else { for y in 0..height { for x in 0..width { let pos = y * width + x; - let color_index = f_lookup.unwrap()(&mut data[(pos * depth) as usize..], depth, - palette, reqcolor, &mut indextable, complexion) as usize; + let color_index = f_lookup.unwrap()( + &mut data[(pos * depth) as usize..], + depth, + palette, + reqcolor, + &mut indextable, + complexion, + ) as usize; result[pos as usize] = color_index as u8; - for n in 0..depth { - let offset = data[(pos * depth + n) as usize] as i32 - palette[color_index * depth as usize + n as usize] as i32; - f_diffuse(&mut data[n as usize..], width, height, x, y, depth, offset); - } - } - } - } - ncolors = reqcolor; - } - - Ok(ncolors) - } - - /* emacs Local Variables: */ - /* emacs mode: c */ - /* emacs tab-width: 4 */ - /* emacs indent-tabs-mode: nil */ - /* emacs c-basic-offset: 4 */ - /* emacs End: */ - /* vim: set expandtab ts=4 sts=4 sw=4 : */ - /* EOF */ - - /* + for n in 0..depth { + let offset = data[(pos * depth + n) as usize] as i32 + - palette[color_index * depth as usize + n as usize] as i32; + f_diffuse(&mut data[n as usize..], width, height, x, y, depth, offset); + } + } + } + } + ncolors = reqcolor; + } + + Ok(ncolors) +} + +/* emacs Local Variables: */ +/* emacs mode: c */ +/* emacs tab-width: 4 */ +/* emacs indent-tabs-mode: nil */ +/* emacs c-basic-offset: 4 */ +/* emacs End: */ +/* vim: set expandtab ts=4 sts=4 sw=4 : */ +/* EOF */ + +/* * * mediancut algorithm implementation is imported from pnmcolormap.c * in netpbm library. @@ -1296,4 +1202,4 @@ pub fn * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * - */ \ No newline at end of file + */ diff --git a/src/tosixel.rs b/src/tosixel.rs index 8a9db24..18ba097 100644 --- a/src/tosixel.rs +++ b/src/tosixel.rs @@ -117,15 +117,12 @@ impl sixel_output { } pub fn put_node( - &mut self, /* output context */ - x: &mut i32, /* header position */ + &mut self, /* output context */ + x: &mut i32, /* header position */ np: sixel_node, /* node object */ - ncolors: i32, /* number of palette colors */ + ncolors: i32, /* number of palette colors */ keycolor: i32, ) -> SixelResult<()> { - - println!("put node pal{}, sx{}, mx{}, map{}", np.pal, np.sx, np.mx, np.map.len()); - if ncolors != 2 || keycolor == -1 { /* designate palette index */ if self.active_palette != np.pal { @@ -315,7 +312,6 @@ impl sixel_output { } let len = ncolors * width as usize; self.active_palette = -1; - println!("colors:{}", ncolors); let mut map: Vec = vec![0; len]; @@ -362,7 +358,6 @@ impl sixel_output { return Err(Box::new(SixelError::BadIntegerOverflow)); } pix = pixels[(check_integer_overflow + x) as usize] as i32; /* color index */ - println!("x:{},y{} pix{},", x, y, pix); if pix >= 0 && (pix as usize) < ncolors && pix != keycolor { if pix > i32::MAX / width { /* integer overflow */ @@ -379,7 +374,6 @@ impl sixel_output { " (pix * width > INT_MAX - x)");*/ return Err(Box::new(SixelError::BadIntegerOverflow)); } - println!("set map at pix:{} width:{} + x:{} to {}\n", pix, width, x, i); map[(pix * width + x) as usize] |= 1 << i; } else if palstate.is_none() { fillable = false; @@ -391,11 +385,10 @@ impl sixel_output { continue; } for c in 0..ncolors { - let mut sx = 0; while sx < width { if map[c * width as usize + sx as usize] == 0 { - sx += 1; + sx += 1; continue; } let mut mx = sx + 1; @@ -424,22 +417,11 @@ impl sixel_output { np.mx = mx; np.map = map[c * width as usize..].to_vec(); - - println!("{}: make node pal {}, sx {}, mx {}, map {}", y, np.pal, np.sx, np.mx, c * width as usize); - for n in 0..width { - print!("0x{:X}, ", np.map[n as usize]); - } - println!(); - self.nodes.insert(0, np); sx = mx - 1; sx += 1; } } - println!("nodes:{}", self.nodes.len()); - for n in &self.nodes { - println!("{}, {}, {}, {}", n.mx, n.sx, n.pal, n.map.len()); - } if y != 5 { /* DECGNL Graphics Next Line */ @@ -448,7 +430,6 @@ impl sixel_output { } let mut x = 0; while self.nodes.len() > 0 { - let mut np = self.nodes.pop().unwrap(); if x > np.sx { @@ -468,8 +449,7 @@ impl sixel_output { } self.put_node(&mut x, np, ncolors as i32, keycolor)?; - - let mut ni: i32 = 0; + let mut ni: i32 = 0; while ni < self.nodes.len() as i32 { let onode = &self.nodes[ni as usize]; @@ -564,12 +544,6 @@ impl sixel_output { dither.apply_palette(pixels, width, height)? } }; - for i in 0..width { - print!("{:x},", input_pixels[i as usize]); - } - println!(""); - - self.encode_header(width, height)?; self.encode_body( &input_pixels, @@ -600,21 +574,21 @@ fn dither_func_fs(data: &mut [u8], width: i32) { let mut r = data[3 + 0] as i32 + ((error_r * 5) >> 4); let mut g = data[3 + 1] as i32 + ((error_g * 5) >> 4); let mut b = data[3 + 2] as i32 + ((error_b * 5) >> 4); - data[3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[3 + 2] = if b > 0xff { 0xff } else { b as u8 }; - r = data[width * 3 - 3 + 0]as i32 + ((error_r * 3) >> 4); - g = data[width * 3 - 3 + 1]as i32 + ((error_g * 3) >> 4); - b = data[width * 3 - 3 + 2]as i32 + ((error_b * 3) >> 4); - data[width * 3 - 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[width * 3 - 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[width * 3 - 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; - r = data[width * 3 + 0]as i32 + ((error_r * 5) >> 4); - g = data[width * 3 + 1]as i32 + ((error_g * 5) >> 4); - b = data[width * 3 + 2]as i32 + ((error_b * 5) >> 4); - data[width * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[width * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[width * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + r = data[width * 3 - 3 + 0] as i32 + ((error_r * 3) >> 4); + g = data[width * 3 - 3 + 1] as i32 + ((error_g * 3) >> 4); + b = data[width * 3 - 3 + 2] as i32 + ((error_b * 3) >> 4); + data[width * 3 - 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[width * 3 - 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[width * 3 - 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + r = data[width * 3 + 0] as i32 + ((error_r * 5) >> 4); + g = data[width * 3 + 1] as i32 + ((error_g * 5) >> 4); + b = data[width * 3 + 2] as i32 + ((error_b * 5) >> 4); + data[width * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[width * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[width * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; } fn dither_func_atkinson(data: &mut [u8], width: i32) { @@ -635,39 +609,39 @@ fn dither_func_atkinson(data: &mut [u8], width: i32) { let mut r = data[(width * 0 + 1) * 3 + 0] as i32 + (error_r >> 3); let mut g = data[(width * 0 + 1) * 3 + 1] as i32 + (error_g >> 3); let mut b = data[(width * 0 + 1) * 3 + 2] as i32 + (error_b >> 3); - data[(width * 0 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 0 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 0 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; - r = data[(width * 0 + 2) * 3 + 0]as i32 + (error_r >> 3); - g = data[(width * 0 + 2) * 3 + 1]as i32 + (error_g >> 3); - b = data[(width * 0 + 2) * 3 + 2]as i32 + (error_b >> 3); - data[(width * 0 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 0 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 0 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; - r = data[(width * 1 - 1) * 3 + 0]as i32 + (error_r >> 3); - g = data[(width * 1 - 1) * 3 + 1]as i32 + (error_g >> 3); - b = data[(width * 1 - 1) * 3 + 2]as i32 + (error_b >> 3); - data[(width * 1 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; - r = data[(width * 1 + 0) * 3 + 0]as i32 + (error_r >> 3); - g = data[(width * 1 + 0) * 3 + 1]as i32 + (error_g >> 3); - b = data[(width * 1 + 0) * 3 + 2]as i32 + (error_b >> 3); - data[(width * 1 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; - r = data[(width * 1 + 1) * 3 + 0]as i32 + (error_r >> 3); - g = data[(width * 1 + 1) * 3 + 1]as i32 + (error_g >> 3); - b = data[(width * 1 + 1) * 3 + 2]as i32 + (error_b >> 3); - data[(width * 1 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; - r = data[(width * 2 + 0) * 3 + 0]as i32 + (error_r >> 3); - g = data[(width * 2 + 0) * 3 + 1]as i32 + (error_g >> 3); - b = data[(width * 2 + 0) * 3 + 2]as i32 + (error_b >> 3); - data[(width * 2 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 0 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 0 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 0 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + r = data[(width * 0 + 2) * 3 + 0] as i32 + (error_r >> 3); + g = data[(width * 0 + 2) * 3 + 1] as i32 + (error_g >> 3); + b = data[(width * 0 + 2) * 3 + 2] as i32 + (error_b >> 3); + data[(width * 0 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 0 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 0 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + r = data[(width * 1 - 1) * 3 + 0] as i32 + (error_r >> 3); + g = data[(width * 1 - 1) * 3 + 1] as i32 + (error_g >> 3); + b = data[(width * 1 - 1) * 3 + 2] as i32 + (error_b >> 3); + data[(width * 1 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + r = data[(width * 1 + 0) * 3 + 0] as i32 + (error_r >> 3); + g = data[(width * 1 + 0) * 3 + 1] as i32 + (error_g >> 3); + b = data[(width * 1 + 0) * 3 + 2] as i32 + (error_b >> 3); + data[(width * 1 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + r = data[(width * 1 + 1) * 3 + 0] as i32 + (error_r >> 3); + g = data[(width * 1 + 1) * 3 + 1] as i32 + (error_g >> 3); + b = data[(width * 1 + 1) * 3 + 2] as i32 + (error_b >> 3); + data[(width * 1 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + r = data[(width * 2 + 0) * 3 + 0] as i32 + (error_r >> 3); + g = data[(width * 2 + 0) * 3 + 1] as i32 + (error_g >> 3); + b = data[(width * 2 + 0) * 3 + 2] as i32 + (error_b >> 3); + data[(width * 2 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; } fn dither_func_jajuni(data: &mut [u8], width: i32) { @@ -687,75 +661,75 @@ fn dither_func_jajuni(data: &mut [u8], width: i32) { let mut r = data[(width * 0 + 1) * 3 + 0] as i32 + (error_r * 7 / 48); let mut g = data[(width * 0 + 1) * 3 + 1] as i32 + (error_g * 7 / 48); let mut b = data[(width * 0 + 1) * 3 + 2] as i32 + (error_b * 7 / 48); - data[(width * 0 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 0 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 0 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 0 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 0 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 0 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 0 + 2) * 3 + 0] as i32 + (error_r * 5 / 48); g = data[(width * 0 + 2) * 3 + 1] as i32 + (error_g * 5 / 48); b = data[(width * 0 + 2) * 3 + 2] as i32 + (error_b * 5 / 48); - data[(width * 0 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 0 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 0 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 0 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 0 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 0 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 - 2) * 3 + 0] as i32 + (error_r * 3 / 48); g = data[(width * 1 - 2) * 3 + 1] as i32 + (error_g * 3 / 48); b = data[(width * 1 - 2) * 3 + 2] as i32 + (error_b * 3 / 48); - data[(width * 1 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 - 1) * 3 + 0] as i32 + (error_r * 5 / 48); g = data[(width * 1 - 1) * 3 + 1] as i32 + (error_g * 5 / 48); b = data[(width * 1 - 1) * 3 + 2] as i32 + (error_b * 5 / 48); - data[(width * 1 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 0) * 3 + 0] as i32 + (error_r * 7 / 48); g = data[(width * 1 + 0) * 3 + 1] as i32 + (error_g * 7 / 48); b = data[(width * 1 + 0) * 3 + 2] as i32 + (error_b * 7 / 48); - data[(width * 1 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 1) * 3 + 0] as i32 + (error_r * 5 / 48); g = data[(width * 1 + 1) * 3 + 1] as i32 + (error_g * 5 / 48); b = data[(width * 1 + 1) * 3 + 2] as i32 + (error_b * 5 / 48); - data[(width * 1 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 2) * 3 + 0] as i32 + (error_r * 3 / 48); g = data[(width * 1 + 2) * 3 + 1] as i32 + (error_g * 3 / 48); b = data[(width * 1 + 2) * 3 + 2] as i32 + (error_b * 3 / 48); - data[(width * 1 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 - 2) * 3 + 0] as i32 + (error_r * 1 / 48); g = data[(width * 2 - 2) * 3 + 1] as i32 + (error_g * 1 / 48); b = data[(width * 2 - 2) * 3 + 2] as i32 + (error_b * 1 / 48); - data[(width * 2 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 - 1) * 3 + 0] as i32 + (error_r * 3 / 48); g = data[(width * 2 - 1) * 3 + 1] as i32 + (error_g * 3 / 48); b = data[(width * 2 - 1) * 3 + 2] as i32 + (error_b * 3 / 48); - data[(width * 2 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 + 0) * 3 + 0] as i32 + (error_r * 5 / 48); g = data[(width * 2 + 0) * 3 + 1] as i32 + (error_g * 5 / 48); b = data[(width * 2 + 0) * 3 + 2] as i32 + (error_b * 5 / 48); - data[(width * 2 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 + 1) * 3 + 0] as i32 + (error_r * 3 / 48); g = data[(width * 2 + 1) * 3 + 1] as i32 + (error_g * 3 / 48); b = data[(width * 2 + 1) * 3 + 2] as i32 + (error_b * 3 / 48); - data[(width * 2 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 + 2) * 3 + 0] as i32 + (error_r * 1 / 48); g = data[(width * 2 + 2) * 3 + 1] as i32 + (error_g * 1 / 48); b = data[(width * 2 + 2) * 3 + 2] as i32 + (error_b * 1 / 48); - data[(width * 2 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; } fn dither_func_stucki(data: &mut [u8], width: i32) { @@ -775,75 +749,75 @@ fn dither_func_stucki(data: &mut [u8], width: i32) { let mut r = data[(width * 0 + 1) * 3 + 0] as i32 + (error_r * 8 / 48); let mut g = data[(width * 0 + 1) * 3 + 1] as i32 + (error_g * 8 / 48); let mut b = data[(width * 0 + 1) * 3 + 2] as i32 + (error_b * 8 / 48); - data[(width * 0 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 0 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 0 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 0 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 0 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 0 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 0 + 2) * 3 + 0] as i32 + (error_r * 4 / 48); g = data[(width * 0 + 2) * 3 + 1] as i32 + (error_g * 4 / 48); b = data[(width * 0 + 2) * 3 + 2] as i32 + (error_b * 4 / 48); - data[(width * 0 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 0 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 0 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 0 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 0 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 0 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 - 2) * 3 + 0] as i32 + (error_r * 2 / 48); g = data[(width * 1 - 2) * 3 + 1] as i32 + (error_g * 2 / 48); b = data[(width * 1 - 2) * 3 + 2] as i32 + (error_b * 2 / 48); - data[(width * 1 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 - 1) * 3 + 0] as i32 + (error_r * 4 / 48); g = data[(width * 1 - 1) * 3 + 1] as i32 + (error_g * 4 / 48); b = data[(width * 1 - 1) * 3 + 2] as i32 + (error_b * 4 / 48); - data[(width * 1 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 0) * 3 + 0] as i32 + (error_r * 8 / 48); g = data[(width * 1 + 0) * 3 + 1] as i32 + (error_g * 8 / 48); b = data[(width * 1 + 0) * 3 + 2] as i32 + (error_b * 8 / 48); - data[(width * 1 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 1) * 3 + 0] as i32 + (error_r * 4 / 48); g = data[(width * 1 + 1) * 3 + 1] as i32 + (error_g * 4 / 48); b = data[(width * 1 + 1) * 3 + 2] as i32 + (error_b * 4 / 48); - data[(width * 1 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 2) * 3 + 0] as i32 + (error_r * 2 / 48); g = data[(width * 1 + 2) * 3 + 1] as i32 + (error_g * 2 / 48); b = data[(width * 1 + 2) * 3 + 2] as i32 + (error_b * 2 / 48); - data[(width * 1 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 - 2) * 3 + 0] as i32 + (error_r * 1 / 48); g = data[(width * 2 - 2) * 3 + 1] as i32 + (error_g * 1 / 48); b = data[(width * 2 - 2) * 3 + 2] as i32 + (error_b * 1 / 48); - data[(width * 2 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 - 1) * 3 + 0] as i32 + (error_r * 2 / 48); g = data[(width * 2 - 1) * 3 + 1] as i32 + (error_g * 2 / 48); b = data[(width * 2 - 1) * 3 + 2] as i32 + (error_b * 2 / 48); - data[(width * 2 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 + 0) * 3 + 0] as i32 + (error_r * 4 / 48); g = data[(width * 2 + 0) * 3 + 1] as i32 + (error_g * 4 / 48); b = data[(width * 2 + 0) * 3 + 2] as i32 + (error_b * 4 / 48); - data[(width * 2 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 + 1) * 3 + 0] as i32 + (error_r * 2 / 48); g = data[(width * 2 + 1) * 3 + 1] as i32 + (error_g * 2 / 48); b = data[(width * 2 + 1) * 3 + 2] as i32 + (error_b * 2 / 48); - data[(width * 2 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 2 + 2) * 3 + 0] as i32 + (error_r * 1 / 48); g = data[(width * 2 + 2) * 3 + 1] as i32 + (error_g * 1 / 48); b = data[(width * 2 + 2) * 3 + 2] as i32 + (error_b * 1 / 48); - data[(width * 2 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 2 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 2 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 2 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 2 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 2 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; } fn dither_func_burkes(data: &mut [u8], width: i32) { @@ -863,45 +837,45 @@ fn dither_func_burkes(data: &mut [u8], width: i32) { let mut r = data[(width * 0 + 1) * 3 + 0] as i32 + (error_r * 4 / 16); let mut g = data[(width * 0 + 1) * 3 + 1] as i32 + (error_g * 4 / 16); let mut b = data[(width * 0 + 1) * 3 + 2] as i32 + (error_b * 4 / 16); - data[(width * 0 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 0 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 0 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 0 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 0 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 0 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 0 + 2) * 3 + 0] as i32 + (error_r * 2 / 16); g = data[(width * 0 + 2) * 3 + 1] as i32 + (error_g * 2 / 16); b = data[(width * 0 + 2) * 3 + 2] as i32 + (error_b * 2 / 16); - data[(width * 0 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 0 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 0 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 0 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 0 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 0 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 - 2) * 3 + 0] as i32 + (error_r * 1 / 16); g = data[(width * 1 - 2) * 3 + 1] as i32 + (error_g * 1 / 16); b = data[(width * 1 - 2) * 3 + 2] as i32 + (error_b * 1 / 16); - data[(width * 1 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 - 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 - 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 - 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 - 1) * 3 + 0] as i32 + (error_r * 2 / 16); g = data[(width * 1 - 1) * 3 + 1] as i32 + (error_g * 2 / 16); b = data[(width * 1 - 1) * 3 + 2] as i32 + (error_b * 2 / 16); - data[(width * 1 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 - 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 - 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 - 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 0) * 3 + 0] as i32 + (error_r * 4 / 16); g = data[(width * 1 + 0) * 3 + 1] as i32 + (error_g * 4 / 16); b = data[(width * 1 + 0) * 3 + 2] as i32 + (error_b * 4 / 16); - data[(width * 1 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 0) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 0) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 0) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 1) * 3 + 0] as i32 + (error_r * 2 / 16); g = data[(width * 1 + 1) * 3 + 1] as i32 + (error_g * 2 / 16); b = data[(width * 1 + 1) * 3 + 2] as i32 + (error_b * 2 / 16); - data[(width * 1 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 1) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 1) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 1) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; r = data[(width * 1 + 2) * 3 + 0] as i32 + (error_r * 1 / 16); g = data[(width * 1 + 2) * 3 + 1] as i32 + (error_g * 1 / 16); b = data[(width * 1 + 2) * 3 + 2] as i32 + (error_b * 1 / 16); - data[(width * 1 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; - data[(width * 1 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; - data[(width * 1 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; + data[(width * 1 + 2) * 3 + 0] = if r > 0xff { 0xff } else { r as u8 }; + data[(width * 1 + 2) * 3 + 1] = if g > 0xff { 0xff } else { g as u8 }; + data[(width * 1 + 2) * 3 + 2] = if b > 0xff { 0xff } else { b as u8 }; } fn dither_func_a_dither(data: &mut [u8], width: i32, x: i32, y: i32) { @@ -1007,7 +981,7 @@ impl sixel_output { let mut marks = vec![false; (width * 6) as usize]; while is_running { let mut dst = 0; - let mut nextpal:usize = 0; + let mut nextpal: usize = 0; let mut threshold = 1; let mut dirty = false; let mut mptr = 0; @@ -1058,7 +1032,7 @@ impl sixel_output { dirty = true; paletted_pixels[dst] = 255; } else { - let pal =nextpal as usize * 3; + let pal = nextpal as usize * 3; rgbhit[pix as usize] = 1; if output_count > 0 {