Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2012 Michael Drake <tlsa@netsurf-browser.org>
  3.  *
  4.  * This file is part of libnsfb, http://www.netsurf-browser.org/
  5.  * Licenced under the MIT License,
  6.  *                http://www.opensource.org/licenses/mit-license.php
  7.  *
  8.  * This is the *internal* interface for the cursor.
  9.  */
  10.  
  11. #ifndef PALETTE_H
  12. #define PALETTE_H 1
  13.  
  14. #include <stdint.h>
  15. #include <limits.h>
  16.  
  17. #include "libnsfb.h"
  18. #include "libnsfb_plot.h"
  19.  
  20. enum nsfb_palette_type_e {
  21.         NSFB_PALETTE_EMPTY,     /**< empty palette object */
  22.         NSFB_PALETTE_NSFB_8BPP, /**< libnsfb's own 8bpp palette */
  23.         NSFB_PALETTE_OTHER      /**< any other palette  */
  24. };
  25.  
  26. struct nsfb_palette_s {
  27.         enum nsfb_palette_type_e type; /**< Palette type */
  28.         uint8_t last; /**< Last used palette index */
  29.         nsfb_colour_t data[256]; /**< Palette for index modes */
  30.  
  31.         bool dither; /**< Whether to use error diffusion */
  32.         struct {
  33.                 int width; /**< Length of error value buffer ring*/
  34.                 int current; /**< Current pos in ring buffer*/
  35.                 int *data; /**< Ring buffer error values */
  36.                 int data_len; /**< Max size of ring */
  37.         } dither_ctx;
  38. };
  39.  
  40.  
  41. /** Create an empty palette object. */
  42. bool nsfb_palette_new(struct nsfb_palette_s **palette, int width);
  43.  
  44. /** Free a palette object. */
  45. void nsfb_palette_free(struct nsfb_palette_s *palette);
  46.  
  47. /** Init error diffusion for a plot. */
  48. void nsfb_palette_dither_init(struct nsfb_palette_s *palette, int width);
  49.  
  50. /** Finalise error diffusion after a plot. */
  51. void nsfb_palette_dither_fini(struct nsfb_palette_s *palette);
  52.  
  53. /** Generate libnsfb 8bpp default palette. */
  54. void nsfb_palette_generate_nsfb_8bpp(struct nsfb_palette_s *palette);
  55.  
  56. /** Find best palette match for given colour. */
  57. static inline uint8_t nsfb_palette_best_match(struct nsfb_palette_s *palette,
  58.                 nsfb_colour_t c, int *r_error, int *g_error, int *b_error)
  59. {
  60.         uint8_t best_col = 0;
  61.  
  62.         nsfb_colour_t palent;
  63.         int col;
  64.         int dr, dg, db; /* delta red, green blue values */
  65.  
  66.         int cur_distance;
  67.         int best_distance = INT_MAX;
  68.  
  69.         switch (palette->type) {
  70.         case NSFB_PALETTE_NSFB_8BPP:
  71.                 /* Index into colour cube part */
  72.                 dr = ((( c        & 0xFF) * 5) + 128) / 256;
  73.                 dg = ((((c >>  8) & 0xFF) * 7) + 128) / 256;
  74.                 db = ((((c >> 16) & 0xFF) * 4) + 128) / 256;
  75.                 col = 40 * dr + 5 * dg + db;
  76.  
  77.                 palent = palette->data[col];
  78.                 dr = ( c        & 0xFF) - ( palent        & 0xFF);
  79.                 dg = ((c >>  8) & 0xFF) - ((palent >> 8 ) & 0xFF);
  80.                 db = ((c >> 16) & 0xFF) - ((palent >> 16) & 0xFF);
  81.                 cur_distance = (dr * dr) + (dg * dg) + (db * db);
  82.  
  83.                 best_col = col;
  84.                 best_distance = cur_distance;
  85.                 *r_error = dr;
  86.                 *g_error = dg;
  87.                 *b_error = db;
  88.  
  89.                 /* Index into grayscale part */
  90.                 col = (( c        & 0xFF) +
  91.                        ((c >>  8) & 0xFF) +
  92.                        ((c >> 16) & 0xFF) + (45 / 2)) / (15 * 3) - 1 + 240;
  93.                 palent = palette->data[col];
  94.  
  95.                 dr = ( c        & 0xFF) - ( palent        & 0xFF);
  96.                 dg = ((c >>  8) & 0xFF) - ((palent >>  8) & 0xFF);
  97.                 db = ((c >> 16) & 0xFF) - ((palent >> 16) & 0xFF);
  98.                 cur_distance = (dr * dr) + (dg * dg) + (db * db);
  99.                 if (cur_distance < best_distance) {
  100.                         best_distance = cur_distance;
  101.                         best_col = col;
  102.                         *r_error = dr;
  103.                         *g_error = dg;
  104.                         *b_error = db;
  105.                 }
  106.                 break;
  107.  
  108.         case NSFB_PALETTE_OTHER:
  109.                 /* Try all colours in palette */
  110.                 for (col = 0; col <= palette->last; col++) {
  111.                         palent = palette->data[col];
  112.  
  113.                         dr = ( c        & 0xFF) - ( palent        & 0xFF);
  114.                         dg = ((c >>  8) & 0xFF) - ((palent >>  8) & 0xFF);
  115.                         db = ((c >> 16) & 0xFF) - ((palent >> 16) & 0xFF);
  116.                         cur_distance = (dr * dr) + (dg * dg) + (db * db);
  117.                         if (cur_distance < best_distance) {
  118.                                 best_distance = cur_distance;
  119.                                 best_col = col;
  120.                                 *r_error = dr;
  121.                                 *g_error = dg;
  122.                                 *b_error = db;
  123.                         }
  124.                 }
  125.                 break;
  126.  
  127.         default:
  128.                 break;
  129.         }
  130.  
  131.         return best_col;
  132. }
  133.  
  134. /** Find best palette match for given colour, with error diffusion. */
  135. static inline uint8_t nsfb_palette_best_match_dither(
  136.                 struct nsfb_palette_s *palette, nsfb_colour_t c)
  137. {
  138.         int r, g, b;
  139.         int current;
  140.         int error;
  141.         int width = palette->dither_ctx.width;
  142.         uint8_t best_col_index;
  143.  
  144.         if (palette == NULL)
  145.                 return 0;
  146.  
  147.         if (palette->dither == false)
  148.                 return nsfb_palette_best_match(palette, c, &r, &g, &b);
  149.  
  150.         current = palette->dither_ctx.current;
  151.  
  152.         /* Get RGB components of colour, and apply error */
  153.         r = ( c        & 0xFF) + palette->dither_ctx.data[current    ];
  154.         g = ((c >>  8) & 0xFF) + palette->dither_ctx.data[current + 1];
  155.         b = ((c >> 16) & 0xFF) + palette->dither_ctx.data[current + 2];
  156.  
  157.         /* Clamp new RGB components to range */
  158.         if (r <   0) r =   0;
  159.         if (r > 255) r = 255;
  160.         if (g <   0) g =   0;
  161.         if (g > 255) g = 255;
  162.         if (b <   0) b =   0;
  163.         if (b > 255) b = 255;
  164.  
  165.         /* Reset error diffusion slots to 0 */
  166.         palette->dither_ctx.data[current    ] = 0;
  167.         palette->dither_ctx.data[current + 1] = 0;
  168.         palette->dither_ctx.data[current + 2] = 0;
  169.  
  170.         /* Rebuild colour from modified components */
  171.         c = r + (g << 8) + (b << 16);
  172.  
  173.         /* Get best match for pixel, and find errors for each component */
  174.         best_col_index = nsfb_palette_best_match(palette, c, &r, &g, &b);
  175.  
  176.         /* Advance one set of error diffusion slots */
  177.         current += 3;
  178.         if (current >= width)
  179.                 current = 0;
  180.         palette->dither_ctx.current = current;
  181.  
  182.         /* Save errors
  183.          *
  184.          *       [*]-[N]
  185.          *      / | \
  186.          *   [l]-[m]-[r]
  187.          */
  188.         error = current;
  189.  
  190.         /* Error for [N] (next) */
  191.         if (error != 0) {
  192.                 /* The pixel exists */
  193.                 palette->dither_ctx.data[error    ] += r * 7 / 16;
  194.                 palette->dither_ctx.data[error + 1] += g * 7 / 16;
  195.                 palette->dither_ctx.data[error + 2] += b * 7 / 16;
  196.         }
  197.  
  198.         error += width - 2 * 3;
  199.         if (error >= width)
  200.                 error -= width;
  201.         /* Error for [l] (below, left) */
  202.         if (error >= 0 && error != 3) {
  203.                 /* The pixel exists */
  204.                 palette->dither_ctx.data[error    ] += r * 3 / 16;
  205.                 palette->dither_ctx.data[error + 1] += g * 3 / 16;
  206.                 palette->dither_ctx.data[error + 2] += b * 3 / 16;
  207.         }
  208.  
  209.         error += 3;
  210.         if (error >= width)
  211.                 error -= width;
  212.         /* Error for [m] (below, middle) */
  213.         palette->dither_ctx.data[error    ] += r * 5 / 16;
  214.         palette->dither_ctx.data[error + 1] += g * 5 / 16;
  215.         palette->dither_ctx.data[error + 2] += b * 5 / 16;
  216.  
  217.         error += 3;
  218.         if (error >= width)
  219.                 error -= width;
  220.         /* Error for [r] (below, right) */
  221.         if (error != 0) {
  222.                 /* The pixel exists */
  223.                 palette->dither_ctx.data[error    ] += r / 16;
  224.                 palette->dither_ctx.data[error + 1] += g / 16;
  225.                 palette->dither_ctx.data[error + 2] += b / 16;
  226.         }
  227.  
  228.         return best_col_index;
  229. }
  230.  
  231. #endif /* PALETTE_H */
  232.