/*
* Copyright 2016 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can
* be found in the LICENSE file.
*
*/
#include <memory.h>
#include "styling.h"
#include "styling_types.h"
#include "skc.h"
//
// FIXME -- x86'isms are temporary
//
#include <immintrin.h>
//
//
//
skc_err
skc_styling_retain(skc_styling_t styling)
{
styling->ref_count += 1;
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_release(skc_styling_t styling)
{
//
// blocks and waits if grid is active
//
styling->release(styling->impl);
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_seal(skc_styling_t styling)
{
//
// no-op if sealed
//
styling->seal(styling->impl);
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_unseal(skc_styling_t styling)
{
//
// no-op if unsealed
// blocks and waits if sealed and grid is active
//
styling->unseal(styling->impl,false);
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_reset(skc_styling_t styling)
{
styling->unseal(styling->impl,true);
styling->layers.count = 0;
styling->groups.count = 0;
styling->extras.count = 0;
return SKC_ERR_SUCCESS;
}
//
// FIXME -- various robustifications can be made to this builder but
// we don't want to make this heavyweight too soon
//
// - out of range layer_id is an error
// - extras[] overflow is an error
//
skc_err
skc_styling_group_alloc(skc_styling_t styling,
skc_group_id * group_id)
{
styling->unseal(styling->impl,true);
*group_id = styling->groups.count++;
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_group_enter(skc_styling_t styling,
skc_group_id group_id,
uint32_t n,
skc_styling_cmd_t const * cmds)
{
styling->unseal(styling->impl,true);
styling->groups.extent[group_id].cmds.enter = styling->extras.count;
memcpy(styling->extras.extent + styling->extras.count,cmds,n * sizeof(*cmds));
styling->extras.count += n;
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_group_leave(skc_styling_t styling,
skc_group_id group_id,
uint32_t n,
skc_styling_cmd_t const * cmds)
{
styling->unseal(styling->impl,true);
styling->groups.extent[group_id].cmds.leave = styling->extras.count;
memcpy(styling->extras.extent + styling->extras.count,cmds,n * sizeof(*cmds));
styling->extras.count += n;
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_group_parents(skc_styling_t styling,
skc_group_id group_id,
uint32_t n,
skc_group_id const * parents)
{
styling->unseal(styling->impl,true);
styling->groups.extent[group_id].parents = (union skc_group_parents)
{
.depth = n, // starts at 0
.base = styling->extras.count
};
while (n-- > 0)
styling->extras.extent[styling->extras.count++].parent = parents[n];
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_group_range_lo(skc_styling_t styling,
skc_group_id group_id,
skc_layer_id layer_lo)
{
styling->unseal(styling->impl,true);
styling->groups.extent[group_id].range.lo = layer_lo;
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_group_range_hi(skc_styling_t styling,
skc_group_id group_id,
skc_layer_id layer_hi)
{
styling->unseal(styling->impl,true);
styling->groups.extent[group_id].range.hi = layer_hi;
return SKC_ERR_SUCCESS;
}
skc_err
skc_styling_group_layer(skc_styling_t styling,
skc_group_id group_id,
skc_layer_id layer_id,
uint32_t n,
skc_styling_cmd_t const * cmds)
{
styling->unseal(styling->impl,true);
styling->layers.extent[layer_id] = (union skc_layer_node)
{
.cmds = styling->extras.count,
.parent = group_id
};
memcpy(styling->extras.extent + styling->extras.count,cmds,n * sizeof(*cmds));
styling->extras.count += n;
return SKC_ERR_SUCCESS;
}
//
// FIXME -- get rid of these x86'isms ASAP -- let compiler figure it
// out with a vector type
//
static
__m128i
skc_convert_colors_4(float const * const colors)
{
__m128i c;
c = _mm_cvtps_ph(*(__m128*)colors,0);
return c;
}
static
__m128i
skc_convert_colors_8(float const * const colors)
{
__m128i c;
c = _mm256_cvtps_ph(*(__m256*)colors,0);
return c;
}
//
//
//
static
void
skc_styling_layer_cmd_rgba_encoder(skc_styling_cmd_t * const cmds,
skc_styling_opcode_e const opcode,
float const rgba[4])
{
__m128i const c = skc_convert_colors_4(rgba);
cmds[0] = opcode;
cmds[1] = c.m128i_u32[0];
cmds[2] = c.m128i_u32[1];
}
void
skc_styling_background_over_encoder(skc_styling_cmd_t * cmds, float const rgba[4])
{
skc_styling_layer_cmd_rgba_encoder(cmds,SKC_STYLING_OPCODE_BACKGROUND_OVER,rgba);
}
void
skc_styling_layer_fill_rgba_encoder(skc_styling_cmd_t * cmds, float const rgba[4])
{
// encode a solid fill
skc_styling_layer_cmd_rgba_encoder(cmds,SKC_STYLING_OPCODE_COLOR_FILL_SOLID,rgba);
}
//
//
//
void
skc_styling_layer_fill_gradient_encoder(skc_styling_cmd_t * cmds,
float x0,
float y0,
float x1,
float y1,
skc_styling_gradient_type_e type,
uint32_t n,
float const stops[],
float const colors[])
{
union skc_styling_cmd * const cmds_u = (union skc_styling_cmd *)cmds;
//
// encode a gradient fill
//
cmds_u[0].opcode = SKC_STYLING_OPCODE_COLOR_FILL_GRADIENT_LINEAR;
float const dx = x1 - x0;
float const dy = y1 - y0;
float const c1 = x0 * dx + y0 * dy;
float const c2 = x1 * dx + y1 * dy;
cmds_u[1].f32 = dx; // dx
cmds_u[2].f32 = -c1; // p0
cmds_u[3].f32 = dy; // dy
cmds_u[4].f32 = 1.0f / (c2 - c1); // denom
//
// store type
//
cmds_u[5].gradient_type = type;
//
// Write out slopes
//
// Note: make sure that that the first and last stop pairs don't
// have a span of zero. Why? Because it's meaningless and the
// zero-span stops can simply be dropped.
//
// And obviously the stops need to monotonically increasing.
//
// These validations can be perfomed elsewhere.
//
// After a pile of simple algebra the slope necessary to map a stop
// percentage on [0,1] to an INDEX.LERP real number from [0.0,N.0]
// is simply:
//
// delta_stop_prev
// slope_curr = --------------- - 1
// delta_stop_curr
//
// Each delta stop equal to zero reduces the stop count by 1.
//
// Note that color pairs are what's stored so this simplified
// representation works for both linear gradients with non-zero
// delta stops and linear gradients that double-up the stops in
// order to produce "stripes".
//
float ds_prev = stops[1] - stops[0];
union skc_styling_cmd * const slopes = cmds_u + 8;
slopes[0].f32 = 1.0f / ds_prev;
uint32_t ds_count = 1;
for (uint32_t ii=1; ii<n-1; ii++)
{
float const ds_curr = stops[ii+1] - stops[ii];
if (ds_curr > 0.0f)
{
slopes[ds_count++].f32 = ds_prev / ds_curr - 1.0f;
ds_prev = ds_curr;
}
}
//
// save a potentially compressed delta slope count
//
cmds_u[6].u32 = ds_count;
cmds_u[7].u32 = n; // REMOVE ME -------------------------------------------- REMOVE
//
// FIXME -- encode color pair as a single color diff as noted by HERB @ CHAP <------------- FIXME
//
//
// write out color pairs while skipping delta stops equal to zero
//
uint32_t const color_count = ds_count + 1;
union skc_styling_cmd * color_r = cmds_u + 8 + ds_count;
union skc_styling_cmd * color_g = color_r + color_count;
union skc_styling_cmd * color_b = color_r + color_count * 2;
union skc_styling_cmd * color_a = color_r + color_count * 3;
for (uint32_t ii=0; ii<n-1; ii++)
{
if (stops[ii+1] > stops[ii])
{
__m128i const c = skc_convert_colors_8(colors+ii*4);
color_r->u16v2.lo = c.m128i_u16[0];
color_r->u16v2.hi = c.m128i_u16[4];
color_g->u16v2.lo = c.m128i_u16[1];
color_g->u16v2.hi = c.m128i_u16[5];
color_b->u16v2.lo = c.m128i_u16[2];
color_b->u16v2.hi = c.m128i_u16[6];
color_a->u16v2.lo = c.m128i_u16[3];
color_a->u16v2.hi = c.m128i_u16[7];
++color_r;
++color_g;
++color_b;
++color_a;
}
}
float laststop[8]; // sentinel to lerp against same color
for (int ii=0; ii<4; ii++)
laststop[ii+4] = laststop[ii] = colors[(n-1)*4+ii];
__m128i const c = skc_convert_colors_8(laststop);
color_r->u16v2.lo = c.m128i_u16[0];
color_r->u16v2.hi = c.m128i_u16[4];
color_g->u16v2.lo = c.m128i_u16[1];
color_g->u16v2.hi = c.m128i_u16[5];
color_b->u16v2.lo = c.m128i_u16[2];
color_b->u16v2.hi = c.m128i_u16[6];
color_a->u16v2.lo = c.m128i_u16[3];
color_a->u16v2.hi = c.m128i_u16[7];
}
//
//
//