1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-19 12:28:22 +01:00
Files
wmaker/wrlib/gradient.c
Christophe CURIS a772f2797d wrlib: add explicit type definition in API to allow compiler Type Checks (3/3)
When defining enums as types instead of simple enums allows to use these
types at the places where the corresponding enum values are expected, then
allowing the compiler to check that, potentially reporting incorrect use
of values to the user.

This patch adds the type for the gradient style for RRender*Gradient.

Signed-off-by: Christophe CURIS <christophe.curis@free.fr>
2014-06-15 19:45:20 +01:00

647 lines
14 KiB
C

/* gradient.c - renders gradients
*
* Raster graphics library
*
* Copyright (c) 1997-2003 Alfredo K. Kojima
* Copyright (c) 1998-2003 Dan Pascu
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <assert.h>
#include "wraster.h"
static RImage *renderHGradient(unsigned width, unsigned height, int r0, int g0, int b0, int rf, int gf, int bf);
static RImage *renderVGradient(unsigned width, unsigned height, int r0, int g0, int b0, int rf, int gf, int bf);
static RImage *renderDGradient(unsigned width, unsigned height, int r0, int g0, int b0, int rf, int gf, int bf);
static RImage *renderMHGradient(unsigned width, unsigned height, RColor ** colors, int count);
static RImage *renderMVGradient(unsigned width, unsigned height, RColor ** colors, int count);
static RImage *renderMDGradient(unsigned width, unsigned height, RColor ** colors, int count);
RImage *RRenderMultiGradient(unsigned width, unsigned height, RColor **colors, RGradientStyle style)
{
int count;
count = 0;
while (colors[count] != NULL)
count++;
if (count > 2) {
switch (style) {
case RHorizontalGradient:
return renderMHGradient(width, height, colors, count);
case RVerticalGradient:
return renderMVGradient(width, height, colors, count);
case RDiagonalGradient:
return renderMDGradient(width, height, colors, count);
}
} else if (count > 1) {
return RRenderGradient(width, height, colors[0], colors[1], style);
} else if (count > 0) {
return RRenderGradient(width, height, colors[0], colors[0], style);
}
assert(0);
return NULL;
}
RImage *RRenderGradient(unsigned width, unsigned height, const RColor *from, const RColor *to, RGradientStyle style)
{
switch (style) {
case RHorizontalGradient:
return renderHGradient(width, height, from->red, from->green,
from->blue, to->red, to->green, to->blue);
case RVerticalGradient:
return renderVGradient(width, height, from->red, from->green,
from->blue, to->red, to->green, to->blue);
case RDiagonalGradient:
return renderDGradient(width, height, from->red, from->green,
from->blue, to->red, to->green, to->blue);
}
assert(0);
return NULL;
}
/*
*----------------------------------------------------------------------
* renderHGradient--
* Renders a horizontal linear gradient of the specified size in the
* RImage format with a border of the specified type.
*
* Returns:
* A 24bit RImage with the gradient (no alpha channel).
*
* Side effects:
* None
*----------------------------------------------------------------------
*/
static RImage *renderHGradient(unsigned width, unsigned height, int r0, int g0, int b0, int rf, int gf, int bf)
{
int i;
long r, g, b, dr, dg, db;
unsigned lineSize = width * 3;
RImage *image;
unsigned char *ptr;
image = RCreateImage(width, height, False);
if (!image) {
return NULL;
}
ptr = image->data;
r = r0 << 16;
g = g0 << 16;
b = b0 << 16;
dr = ((rf - r0) << 16) / (int)width;
dg = ((gf - g0) << 16) / (int)width;
db = ((bf - b0) << 16) / (int)width;
/* render the first line */
for (i = 0; i < width; i++) {
*(ptr++) = (unsigned char)(r >> 16);
*(ptr++) = (unsigned char)(g >> 16);
*(ptr++) = (unsigned char)(b >> 16);
r += dr;
g += dg;
b += db;
}
/* copy the first line to the other lines */
for (i = 1; i < height; i++) {
memcpy(&(image->data[i * lineSize]), image->data, lineSize);
}
return image;
}
/*
*----------------------------------------------------------------------
* renderVGradient--
* Renders a vertical linear gradient of the specified size in the
* RImage format with a border of the specified type.
*
* Returns:
* A 24bit RImage with the gradient (no alpha channel).
*
* Side effects:
* None
*----------------------------------------------------------------------
*/
static RImage *renderVGradient(unsigned width, unsigned height, int r0, int g0, int b0, int rf, int gf, int bf)
{
int i, j;
long r, g, b, dr, dg, db;
RImage *image;
unsigned char *ptr;
unsigned char rr, gg, bb;
image = RCreateImage(width, height, False);
if (!image) {
return NULL;
}
ptr = image->data;
r = r0 << 16;
g = g0 << 16;
b = b0 << 16;
dr = ((rf - r0) << 16) / (int)height;
dg = ((gf - g0) << 16) / (int)height;
db = ((bf - b0) << 16) / (int)height;
for (i = 0; i < height; i++) {
rr = r >> 16;
gg = g >> 16;
bb = b >> 16;
for (j = 0; j < width / 8; j++) {
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
}
switch (width % 8) {
case 7:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 6:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 5:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 4:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 3:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 2:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 1:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
}
r += dr;
g += dg;
b += db;
}
return image;
}
/*
*----------------------------------------------------------------------
* renderDGradient--
* Renders a diagonal linear gradient of the specified size in the
* RImage format with a border of the specified type.
*
* Returns:
* A 24bit RImage with the gradient (no alpha channel).
*
* Side effects:
* None
*----------------------------------------------------------------------
*/
static RImage *renderDGradient(unsigned width, unsigned height, int r0, int g0, int b0, int rf, int gf, int bf)
{
RImage *image, *tmp;
int j;
float a, offset;
unsigned char *ptr;
if (width == 1)
return renderVGradient(width, height, r0, g0, b0, rf, gf, bf);
else if (height == 1)
return renderHGradient(width, height, r0, g0, b0, rf, gf, bf);
image = RCreateImage(width, height, False);
if (!image) {
return NULL;
}
tmp = renderHGradient(2 * width - 1, 1, r0, g0, b0, rf, gf, bf);
if (!tmp) {
RReleaseImage(image);
return NULL;
}
ptr = tmp->data;
a = ((float)(width - 1)) / ((float)(height - 1));
width = width * 3;
/* copy the first line to the other lines with corresponding offset */
for (j = 0, offset = 0.0; j < width * height; j += width) {
memcpy(&(image->data[j]), &ptr[3 * (int)offset], width);
offset += a;
}
RReleaseImage(tmp);
return image;
}
static RImage *renderMHGradient(unsigned width, unsigned height, RColor ** colors, int count)
{
int i, j, k;
long r, g, b, dr, dg, db;
unsigned lineSize = width * 3;
RImage *image;
unsigned char *ptr;
unsigned width2;
assert(count > 2);
image = RCreateImage(width, height, False);
if (!image) {
return NULL;
}
ptr = image->data;
if (count > width)
count = width;
if (count > 1)
width2 = width / (count - 1);
else
width2 = width;
k = 0;
r = colors[0]->red << 16;
g = colors[0]->green << 16;
b = colors[0]->blue << 16;
/* render the first line */
for (i = 1; i < count; i++) {
dr = ((int)(colors[i]->red - colors[i - 1]->red) << 16) / (int)width2;
dg = ((int)(colors[i]->green - colors[i - 1]->green) << 16) / (int)width2;
db = ((int)(colors[i]->blue - colors[i - 1]->blue) << 16) / (int)width2;
for (j = 0; j < width2; j++) {
*ptr++ = (unsigned char)(r >> 16);
*ptr++ = (unsigned char)(g >> 16);
*ptr++ = (unsigned char)(b >> 16);
r += dr;
g += dg;
b += db;
k++;
}
r = colors[i]->red << 16;
g = colors[i]->green << 16;
b = colors[i]->blue << 16;
}
for (j = k; j < width; j++) {
*ptr++ = (unsigned char)(r >> 16);
*ptr++ = (unsigned char)(g >> 16);
*ptr++ = (unsigned char)(b >> 16);
}
/* copy the first line to the other lines */
for (i = 1; i < height; i++) {
memcpy(&(image->data[i * lineSize]), image->data, lineSize);
}
return image;
}
static RImage *renderMVGradient(unsigned width, unsigned height, RColor ** colors, int count)
{
int i, j, k;
long r, g, b, dr, dg, db;
unsigned lineSize = width * 3;
RImage *image;
unsigned char *ptr, *tmp;
unsigned height2;
int x;
unsigned char rr, gg, bb;
assert(count > 2);
image = RCreateImage(width, height, False);
if (!image) {
return NULL;
}
ptr = image->data;
if (count > height)
count = height;
if (count > 1)
height2 = height / (count - 1);
else
height2 = height;
k = 0;
r = colors[0]->red << 16;
g = colors[0]->green << 16;
b = colors[0]->blue << 16;
for (i = 1; i < count; i++) {
dr = ((int)(colors[i]->red - colors[i - 1]->red) << 16) / (int)height2;
dg = ((int)(colors[i]->green - colors[i - 1]->green) << 16) / (int)height2;
db = ((int)(colors[i]->blue - colors[i - 1]->blue) << 16) / (int)height2;
for (j = 0; j < height2; j++) {
rr = r >> 16;
gg = g >> 16;
bb = b >> 16;
for (x = 0; x < width / 4; x++) {
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
}
switch (width % 4) {
case 3:
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
case 2:
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
case 1:
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
}
r += dr;
g += dg;
b += db;
k++;
}
r = colors[i]->red << 16;
g = colors[i]->green << 16;
b = colors[i]->blue << 16;
}
rr = r >> 16;
gg = g >> 16;
bb = b >> 16;
if (k < height) {
tmp = ptr;
for (x = 0; x < width / 4; x++) {
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
}
switch (width % 4) {
case 3:
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
case 2:
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
case 1:
*ptr++ = rr;
*ptr++ = gg;
*ptr++ = bb;
default:
break;
}
for (j = k + 1; j < height; j++) {
memcpy(ptr, tmp, lineSize);
ptr += lineSize;
}
}
return image;
}
static RImage *renderMDGradient(unsigned width, unsigned height, RColor ** colors, int count)
{
RImage *image, *tmp;
float a, offset;
int j;
unsigned char *ptr;
assert(count > 2);
if (width == 1)
return renderMVGradient(width, height, colors, count);
else if (height == 1)
return renderMHGradient(width, height, colors, count);
image = RCreateImage(width, height, False);
if (!image) {
return NULL;
}
if (count > width)
count = width;
if (count > height)
count = height;
if (count > 2)
tmp = renderMHGradient(2 * width - 1, 1, colors, count);
else
tmp = renderHGradient(2 * width - 1, 1, colors[0]->red << 8,
colors[0]->green << 8, colors[0]->blue << 8,
colors[1]->red << 8, colors[1]->green << 8, colors[1]->blue << 8);
if (!tmp) {
RReleaseImage(image);
return NULL;
}
ptr = tmp->data;
a = ((float)(width - 1)) / ((float)(height - 1));
width = width * 3;
/* copy the first line to the other lines with corresponding offset */
for (j = 0, offset = 0; j < width * height; j += width) {
memcpy(&(image->data[j]), &ptr[3 * (int)offset], width);
offset += a;
}
RReleaseImage(tmp);
return image;
}
RImage *RRenderInterwovenGradient(unsigned width, unsigned height,
RColor colors1[2], int thickness1, RColor colors2[2], int thickness2)
{
int i, j, k, l, ll;
long r1, g1, b1, dr1, dg1, db1;
long r2, g2, b2, dr2, dg2, db2;
RImage *image;
unsigned char *ptr;
unsigned char rr, gg, bb;
image = RCreateImage(width, height, False);
if (!image) {
return NULL;
}
ptr = image->data;
r1 = colors1[0].red << 16;
g1 = colors1[0].green << 16;
b1 = colors1[0].blue << 16;
r2 = colors2[0].red << 16;
g2 = colors2[0].green << 16;
b2 = colors2[0].blue << 16;
dr1 = ((colors1[1].red - colors1[0].red) << 16) / (int)height;
dg1 = ((colors1[1].green - colors1[0].green) << 16) / (int)height;
db1 = ((colors1[1].blue - colors1[0].blue) << 16) / (int)height;
dr2 = ((colors2[1].red - colors2[0].red) << 16) / (int)height;
dg2 = ((colors2[1].green - colors2[0].green) << 16) / (int)height;
db2 = ((colors2[1].blue - colors2[0].blue) << 16) / (int)height;
for (i = 0, k = 0, l = 0, ll = thickness1; i < height; i++) {
if (k == 0) {
rr = r1 >> 16;
gg = g1 >> 16;
bb = b1 >> 16;
} else {
rr = r2 >> 16;
gg = g2 >> 16;
bb = b2 >> 16;
}
for (j = 0; j < width / 8; j++) {
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
}
switch (width % 8) {
case 7:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 6:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 5:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 4:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 3:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 2:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
case 1:
*(ptr++) = rr;
*(ptr++) = gg;
*(ptr++) = bb;
}
if (++l == ll) {
if (k == 0) {
k = 1;
ll = thickness2;
} else {
k = 0;
ll = thickness1;
}
l = 0;
}
r1 += dr1;
g1 += dg1;
b1 += db1;
r2 += dr2;
g2 += dg2;
b2 += db2;
}
return image;
}