summaryrefslogtreecommitdiff
path: root/gs/base/gscicach.c
diff options
context:
space:
mode:
Diffstat (limited to 'gs/base/gscicach.c')
-rw-r--r--gs/base/gscicach.c291
1 files changed, 291 insertions, 0 deletions
diff --git a/gs/base/gscicach.c b/gs/base/gscicach.c
new file mode 100644
index 000000000..33688d7a8
--- /dev/null
+++ b/gs/base/gscicach.c
@@ -0,0 +1,291 @@
+/* Copyright (C) 2001-2007 Artifex Software, Inc.
+ All Rights Reserved.
+
+ This software is provided AS-IS with no warranty, either express or
+ implied.
+
+ This software is distributed under license and may not be copied, modified
+ or distributed except as expressly authorized under the terms of that
+ license. Refer to licensing information at http://www.artifex.com/
+ or contact Artifex Software, Inc., 7 Mt. Lassen Drive - Suite A-134,
+ San Rafael, CA 94903, U.S.A., +1(415)492-9861, for further information.
+*/
+
+/* $Id$ */
+/* A color index cache. */
+#include "gx.h"
+#include "gserrors.h"
+#include "gsccolor.h"
+#include "gxcspace.h"
+#include "gxdcolor.h"
+#include "gscicach.h"
+#include "memory_.h"
+
+#define COLOR_INDEX_CACHE_SIZE 256
+#define COLOR_INDEX_CACHE_CHAINS (COLOR_INDEX_CACHE_SIZE / 16)
+
+typedef struct gs_color_index_cache_elem_s gs_color_index_cache_elem_t;
+
+struct gs_color_index_cache_elem_s {
+ gx_color_index cindex;
+ uint chain;
+ uint prev, next; /* NULL for unused. */
+ uint touch_prev, touch_next;
+ bool frac_values_done;
+};
+
+struct gs_color_index_cache_s {
+ const gs_color_space *direct_space;
+ gs_imager_state *pis;
+ gx_device *dev;
+ int client_num_components;
+ int device_num_components;
+ gs_memory_t *memory;
+ int used;
+ gs_color_index_cache_elem_t *buf;
+ uint recent_touch;
+ float *paint_values;
+ frac31 *frac_values;
+ int chains[COLOR_INDEX_CACHE_CHAINS];
+ /* Note : the 0th element of buf, paint_values, frac_values is never used,
+ because we consider the index 0 as NULL
+ just for a faster initialization. */
+# define MYNULL 0
+};
+
+
+
+gs_private_st_ptrs5(st_color_index_cache, gs_color_index_cache_t, "gs_color_index_cache_t",
+ gs_color_index_cache_elem_ptrs, gs_color_index_cache_reloc_ptrs,
+ direct_space, memory, buf, paint_values, frac_values);
+
+gs_color_index_cache_t *
+gs_color_index_cache_create(gs_memory_t *memory, const gs_color_space *direct_space, gx_device *dev, gs_imager_state *pis, bool need_frac)
+{
+ int client_num_components = cs_num_components(direct_space);
+ int device_num_components = dev->color_info.num_components;
+ gs_color_index_cache_elem_t *buf = ( gs_color_index_cache_elem_t *)gs_alloc_byte_array(memory, COLOR_INDEX_CACHE_SIZE,
+ sizeof(gs_color_index_cache_elem_t), "gs_color_index_cache_create");
+ float *paint_values = (float *)gs_alloc_byte_array(memory, COLOR_INDEX_CACHE_SIZE * client_num_components,
+ sizeof(float), "gs_color_index_cache_create");
+ frac31 *frac_values = (need_frac ? (frac31 *)gs_alloc_byte_array(memory, COLOR_INDEX_CACHE_SIZE * device_num_components,
+ sizeof(frac31), "gs_color_index_cache_create") : NULL);
+ gs_color_index_cache_t *pcic = gs_alloc_struct(memory, gs_color_index_cache_t, &st_color_index_cache, "gs_color_index_cache_create");
+
+ if (buf == NULL || paint_values == NULL || (need_frac && frac_values == NULL) || pcic == NULL) {
+ gs_free_object(memory, buf, "gs_color_index_cache_create");
+ gs_free_object(memory, paint_values, "gs_color_index_cache_create");
+ gs_free_object(memory, frac_values, "gs_color_index_cache_create");
+ gs_free_object(memory, pcic, "gs_color_index_cache_create");
+ return NULL;
+ }
+ memset(pcic, 0, sizeof(*pcic));
+ memset(buf, 0, COLOR_INDEX_CACHE_SIZE * sizeof(gs_color_index_cache_elem_t));
+ pcic->direct_space = direct_space;
+ pcic->pis = pis;
+ pcic->dev = dev;
+ pcic->device_num_components = device_num_components;
+ pcic->client_num_components = client_num_components;
+ pcic->memory = memory;
+ pcic->used = 1; /* Never use the 0th element. */
+ pcic->buf = buf;
+ pcic->recent_touch = MYNULL;
+ pcic->paint_values = paint_values;
+ pcic->frac_values = frac_values;
+ return pcic;
+}
+
+void
+gs_color_index_cache_destroy(gs_color_index_cache_t *pcic)
+{
+ gs_free_object(pcic->memory, pcic->buf, "gs_color_index_cache_create");
+ gs_free_object(pcic->memory, pcic->paint_values, "gs_color_index_cache_create");
+ gs_free_object(pcic->memory, pcic->frac_values, "gs_color_index_cache_create");
+ pcic->buf = NULL;
+ pcic->paint_values = NULL;
+ pcic->frac_values = NULL;
+ gs_free_object(pcic->memory, pcic, "gs_color_index_cache_create");
+}
+
+static inline int
+hash_paint_values(const gs_color_index_cache_t *this, const float *paint_values)
+{
+ int i;
+ float v = 0;
+ uint k = 0;
+ const uint a_prime = 79;
+
+ for (i = 0; i < this->client_num_components; i++)
+ v = v * a_prime + paint_values[i];
+ /* Don't know the range of v, so hash its bytes : */
+ for(i = 0; i < sizeof(v); i++)
+ k = k * a_prime + ((byte *)&v)[i];
+ return k % COLOR_INDEX_CACHE_CHAINS;
+}
+
+static inline void
+exclude_from_chain(gs_color_index_cache_t *this, uint i)
+{
+ uint co = this->buf[i].chain;
+ uint ip = this->buf[i].prev, in = this->buf[i].next;
+
+ this->buf[ip].next = in;
+ this->buf[in].prev = ip;
+ if (this->chains[co] == i)
+ this->chains[co] = in;
+}
+
+static inline void
+include_into_chain(gs_color_index_cache_t *this, uint i, uint c)
+{
+ if (this->chains[c] != MYNULL) {
+ uint in = this->chains[c], ip = this->buf[in].prev;
+
+ this->buf[i].next = in;
+ this->buf[i].prev = ip;
+ this->buf[in].prev = i;
+ this->buf[ip].next = i;
+ } else
+ this->buf[i].prev = this->buf[i].next = i;
+ this->chains[c] = i;
+ this->buf[i].chain = c;
+}
+
+static inline void
+exclude_from_touch_list(gs_color_index_cache_t *this, uint i)
+{
+ uint ip = this->buf[i].touch_prev, in = this->buf[i].touch_next;
+
+ this->buf[ip].touch_next = in;
+ this->buf[in].touch_prev = ip;
+ if (this->recent_touch == i) {
+ if (i == in)
+ this->recent_touch = MYNULL;
+ else
+ this->recent_touch = in;
+ }
+}
+
+static inline void
+include_into_touch_list(gs_color_index_cache_t *this, uint i)
+{
+ if (this->recent_touch != MYNULL) {
+ uint in = this->recent_touch, ip = this->buf[in].touch_prev;
+
+ this->buf[i].touch_next = in;
+ this->buf[i].touch_prev = ip;
+ this->buf[in].touch_prev = i;
+ this->buf[ip].touch_next = i;
+ } else
+ this->buf[i].touch_prev = this->buf[i].touch_next = i;
+ this->recent_touch = i;
+}
+
+static int
+get_color_index_cache_elem(gs_color_index_cache_t *this, const float *paint_values, uint *pi)
+{
+ int client_num_components = this->client_num_components;
+ uint c = hash_paint_values(this, paint_values);
+ uint i = this->chains[c], j;
+
+ if (i != MYNULL) {
+ uint tries = 16; /* Arbitrary. */
+
+ if (!memcmp(paint_values, this->paint_values + i * client_num_components, sizeof(*paint_values) * client_num_components)) {
+ if (this->recent_touch != i) {
+ exclude_from_touch_list(this, i);
+ include_into_touch_list(this, i);
+ }
+ *pi = i;
+ return 1;
+ }
+ for (j = this->buf[i].next; tries -- && j != i; j = this->buf[j].next) {
+ if (!memcmp(paint_values, this->paint_values + j * client_num_components, sizeof(*paint_values) * client_num_components)) {
+ exclude_from_chain(this, j);
+ include_into_chain(this, j, c);
+ if (this->recent_touch != j) {
+ exclude_from_touch_list(this, j);
+ include_into_touch_list(this, j);
+ }
+ *pi = j;
+ return 1;
+ }
+ }
+ tries+=0;
+ }
+ if (this->used < COLOR_INDEX_CACHE_SIZE) {
+ /* Use a new one */
+ i = this->used++;
+ include_into_touch_list(this, i);
+ } else {
+ i = this->recent_touch;
+ this->recent_touch = this->buf[i].touch_prev; /* Assuming the cyclic list,
+ just move the head pointer to the last element. */
+ exclude_from_chain(this, i);
+ }
+ include_into_chain(this, i, c);
+ *pi = i;
+ return 0;
+}
+
+static inline void
+compute_frac_values(gs_color_index_cache_t *this, uint i)
+{
+ gx_color_index c = this->buf[i].cindex;
+ const gx_device_color_info *cinfo = &this->dev->color_info;
+ int device_num_components = this->device_num_components;
+ int j;
+
+ for (j = 0; j < device_num_components; j++) {
+ int shift = cinfo->comp_shift[j];
+ int bits = cinfo->comp_bits[j];
+
+ this->frac_values[i * device_num_components + j] = ((c >> shift) & ((1 << bits) - 1)) << (sizeof(frac31) * 8 - 1 - bits);
+ }
+ this->buf[i].frac_values_done = true;
+}
+
+int
+gs_cached_color_index(gs_color_index_cache_t *this, const float *paint_values, gx_device_color *pdevc, frac31 *frac_values)
+{
+ /* Must return 2 if the color is not pure.
+ See patch_color_to_device_color. */
+ const gs_color_space *pcs = this->direct_space;
+ int client_num_components = this->client_num_components;
+ int device_num_components = this->device_num_components;
+ uint i;
+ int code;
+
+ if (get_color_index_cache_elem(this, paint_values, &i)) {
+ if (pdevc != NULL) {
+ pdevc->colors.pure = this->buf[i].cindex;
+ pdevc->type = &gx_dc_type_data_pure;
+ memcpy(pdevc->ccolor.paint.values, paint_values, sizeof(*paint_values) * client_num_components);
+ pdevc->ccolor_valid = true;
+ }
+ if (frac_values != NULL && !this->buf[i].frac_values_done)
+ compute_frac_values(this, i);
+ } else {
+ gx_device_color devc_local;
+ gs_client_color fcc;
+
+ if (pdevc == NULL)
+ pdevc = &devc_local;
+ memcpy(this->paint_values + i * client_num_components, paint_values, sizeof(*paint_values) * client_num_components);
+ memcpy(fcc.paint.values, paint_values, sizeof(*paint_values) * client_num_components);
+ code = pcs->type->remap_color(&fcc, pcs, pdevc, this->pis, this->dev, gs_color_select_texture);
+ if (code < 0)
+ return code;
+ if (pdevc->type != &gx_dc_type_data_pure)
+ return 2;
+ this->buf[i].cindex = pdevc->colors.pure;
+
+ if (frac_values != NULL)
+ compute_frac_values(this, i);
+ else
+ this->buf[i].frac_values_done = false;
+ }
+ if (frac_values != NULL)
+ memcpy(frac_values, this->frac_values + i * device_num_components, sizeof(*frac_values) * device_num_components);
+ return 0;
+}