kolibrios/programs/network/netsurf/libnsfb/include/palette.h

232 lines
6.2 KiB
C
Raw Normal View History

/*
* Copyright 2012 Michael Drake <tlsa@netsurf-browser.org>
*
* This file is part of libnsfb, http://www.netsurf-browser.org/
* Licenced under the MIT License,
* http://www.opensource.org/licenses/mit-license.php
*
* This is the *internal* interface for the cursor.
*/
#ifndef PALETTE_H
#define PALETTE_H 1
#include <stdint.h>
#include <limits.h>
#include "libnsfb.h"
#include "libnsfb_plot.h"
enum nsfb_palette_type_e {
NSFB_PALETTE_EMPTY, /**< empty palette object */
NSFB_PALETTE_NSFB_8BPP, /**< libnsfb's own 8bpp palette */
NSFB_PALETTE_OTHER /**< any other palette */
};
struct nsfb_palette_s {
enum nsfb_palette_type_e type; /**< Palette type */
uint8_t last; /**< Last used palette index */
nsfb_colour_t data[256]; /**< Palette for index modes */
bool dither; /**< Whether to use error diffusion */
struct {
int width; /**< Length of error value buffer ring*/
int current; /**< Current pos in ring buffer*/
int *data; /**< Ring buffer error values */
int data_len; /**< Max size of ring */
} dither_ctx;
};
/** Create an empty palette object. */
bool nsfb_palette_new(struct nsfb_palette_s **palette, int width);
/** Free a palette object. */
void nsfb_palette_free(struct nsfb_palette_s *palette);
/** Init error diffusion for a plot. */
void nsfb_palette_dither_init(struct nsfb_palette_s *palette, int width);
/** Finalise error diffusion after a plot. */
void nsfb_palette_dither_fini(struct nsfb_palette_s *palette);
/** Generate libnsfb 8bpp default palette. */
void nsfb_palette_generate_nsfb_8bpp(struct nsfb_palette_s *palette);
/** Find best palette match for given colour. */
static inline uint8_t nsfb_palette_best_match(struct nsfb_palette_s *palette,
nsfb_colour_t c, int *r_error, int *g_error, int *b_error)
{
uint8_t best_col = 0;
nsfb_colour_t palent;
int col;
int dr, dg, db; /* delta red, green blue values */
int cur_distance;
int best_distance = INT_MAX;
switch (palette->type) {
case NSFB_PALETTE_NSFB_8BPP:
/* Index into colour cube part */
dr = ((( c & 0xFF) * 5) + 128) / 256;
dg = ((((c >> 8) & 0xFF) * 7) + 128) / 256;
db = ((((c >> 16) & 0xFF) * 4) + 128) / 256;
col = 40 * dr + 5 * dg + db;
palent = palette->data[col];
dr = ( c & 0xFF) - ( palent & 0xFF);
dg = ((c >> 8) & 0xFF) - ((palent >> 8 ) & 0xFF);
db = ((c >> 16) & 0xFF) - ((palent >> 16) & 0xFF);
cur_distance = (dr * dr) + (dg * dg) + (db * db);
best_col = col;
best_distance = cur_distance;
*r_error = dr;
*g_error = dg;
*b_error = db;
/* Index into grayscale part */
col = (( c & 0xFF) +
((c >> 8) & 0xFF) +
((c >> 16) & 0xFF) + (45 / 2)) / (15 * 3) - 1 + 240;
palent = palette->data[col];
dr = ( c & 0xFF) - ( palent & 0xFF);
dg = ((c >> 8) & 0xFF) - ((palent >> 8) & 0xFF);
db = ((c >> 16) & 0xFF) - ((palent >> 16) & 0xFF);
cur_distance = (dr * dr) + (dg * dg) + (db * db);
if (cur_distance < best_distance) {
best_distance = cur_distance;
best_col = col;
*r_error = dr;
*g_error = dg;
*b_error = db;
}
break;
case NSFB_PALETTE_OTHER:
/* Try all colours in palette */
for (col = 0; col <= palette->last; col++) {
palent = palette->data[col];
dr = ( c & 0xFF) - ( palent & 0xFF);
dg = ((c >> 8) & 0xFF) - ((palent >> 8) & 0xFF);
db = ((c >> 16) & 0xFF) - ((palent >> 16) & 0xFF);
cur_distance = (dr * dr) + (dg * dg) + (db * db);
if (cur_distance < best_distance) {
best_distance = cur_distance;
best_col = col;
*r_error = dr;
*g_error = dg;
*b_error = db;
}
}
break;
default:
break;
}
return best_col;
}
/** Find best palette match for given colour, with error diffusion. */
static inline uint8_t nsfb_palette_best_match_dither(
struct nsfb_palette_s *palette, nsfb_colour_t c)
{
int r, g, b;
int current;
int error;
int width = palette->dither_ctx.width;
uint8_t best_col_index;
if (palette == NULL)
return 0;
if (palette->dither == false)
return nsfb_palette_best_match(palette, c, &r, &g, &b);
current = palette->dither_ctx.current;
/* Get RGB components of colour, and apply error */
r = ( c & 0xFF) + palette->dither_ctx.data[current ];
g = ((c >> 8) & 0xFF) + palette->dither_ctx.data[current + 1];
b = ((c >> 16) & 0xFF) + palette->dither_ctx.data[current + 2];
/* Clamp new RGB components to range */
if (r < 0) r = 0;
if (r > 255) r = 255;
if (g < 0) g = 0;
if (g > 255) g = 255;
if (b < 0) b = 0;
if (b > 255) b = 255;
/* Reset error diffusion slots to 0 */
palette->dither_ctx.data[current ] = 0;
palette->dither_ctx.data[current + 1] = 0;
palette->dither_ctx.data[current + 2] = 0;
/* Rebuild colour from modified components */
c = r + (g << 8) + (b << 16);
/* Get best match for pixel, and find errors for each component */
best_col_index = nsfb_palette_best_match(palette, c, &r, &g, &b);
/* Advance one set of error diffusion slots */
current += 3;
if (current >= width)
current = 0;
palette->dither_ctx.current = current;
/* Save errors
*
* [*]-[N]
* / | \
* [l]-[m]-[r]
*/
error = current;
/* Error for [N] (next) */
if (error != 0) {
/* The pixel exists */
palette->dither_ctx.data[error ] += r * 7 / 16;
palette->dither_ctx.data[error + 1] += g * 7 / 16;
palette->dither_ctx.data[error + 2] += b * 7 / 16;
}
error += width - 2 * 3;
if (error >= width)
error -= width;
/* Error for [l] (below, left) */
if (error >= 0 && error != 3) {
/* The pixel exists */
palette->dither_ctx.data[error ] += r * 3 / 16;
palette->dither_ctx.data[error + 1] += g * 3 / 16;
palette->dither_ctx.data[error + 2] += b * 3 / 16;
}
error += 3;
if (error >= width)
error -= width;
/* Error for [m] (below, middle) */
palette->dither_ctx.data[error ] += r * 5 / 16;
palette->dither_ctx.data[error + 1] += g * 5 / 16;
palette->dither_ctx.data[error + 2] += b * 5 / 16;
error += 3;
if (error >= width)
error -= width;
/* Error for [r] (below, right) */
if (error != 0) {
/* The pixel exists */
palette->dither_ctx.data[error ] += r / 16;
palette->dither_ctx.data[error + 1] += g / 16;
palette->dither_ctx.data[error + 2] += b / 16;
}
return best_col_index;
}
#endif /* PALETTE_H */