File Coverage

include/in.h
Criterion Covered Total %
statement 66 67 98.5
branch 37 54 68.5
condition n/a
subroutine n/a
pod n/a
total 103 121 85.1


line stmt bran cond sub pod time code
1             /*
2             * in.h - Perl-XS bridge helpers for Colouring::In::XS
3             *
4             * Provides the glue between Perl SVs/HVs/AVs and the pure-C
5             * colouring_rgba_t from colouring.h. Header-only; requires
6             * PERL_NO_GET_CONTEXT, perl.h and XSUB.h already included.
7             *
8             * Re-usable by Eshu (CSS preprocessor) or any other XS module
9             * that works with Colouring::In colour objects.
10             */
11              
12             #ifndef COLOURING_IN_H
13             #define COLOURING_IN_H
14              
15             #include "colouring.h"
16              
17             /* ── Class name constant ──────────────────────────────────────── */
18              
19             #define COLOURING_CLASS "Colouring::In::XS"
20             #define COLOURING_CLASS_LEN 17
21              
22             /* ── Message store (set from Perl side) ───────────────────────── */
23              
24             static HV * MESSAGES;
25              
26             /* ── Bless a hash into the caller's class ─────────────────────── */
27              
28 10151           static SV * xs_new(SV * class, HV * hash) {
29             dTHX;
30 10151 100         if (SvROK(class)) {
31 2 50         char * name = HvNAME(SvSTASH(SvRV(class)));
    50          
    50          
    0          
    50          
    50          
32 2           class = newSVpv(name, strlen(name));
33             }
34 10151           return sv_bless(newRV_noinc((SV*)hash), gv_stashsv(class, 0));
35             }
36              
37             /* ── Quick "does this SV look numeric?" check ─────────────────── */
38              
39 10160           static int numIs(SV * num) {
40             dTHX;
41 10160           char * str = SvPV_nolen(num);
42             char tmp[256];
43             int i;
44 10160           tmp[0] = '\0';
45 10160 50         for (i = 0; str[i]; i++) {
46 10160           int j = 0;
47 20320 100         while (str[i] >= '0' && str[i] <= '9') {
    50          
48 10160           tmp[j] = str[i];
49 10160           i++;
50 10160           j++;
51             }
52 10160           break;
53             }
54 10160           return strlen(tmp) >= 1 ? 1 : 0;
55             }
56              
57             /* ── Scale a value that may be a percentage ───────────────────── */
58              
59 3           static double xs_scaled(SV * num, int size) {
60             dTHX;
61 3           char * number = SvPV_nolen(num);
62 3           double n = atof(number);
63 3 50         if (number[strlen(number)] == '%') {
64 0           return (n * size) / 100;
65             }
66 3           return n;
67             }
68              
69             /* ── Extract RGBA from a blessed Colouring::In::XS object ─────── */
70              
71 181           static colouring_rgba_t xs_extract_rgba(SV * self) {
72             dTHX;
73             colouring_rgba_t c;
74 181           AV * colour = (AV*)SvRV(*hv_fetch((HV*)SvRV(self), "colour", 6, 0));
75 181           int len = av_len(colour);
76             SV *r, *g, *b;
77              
78 181 50         r = len >= 0 ? *av_fetch(colour, 0, 0) : NULL;
79 181 50         g = len >= 1 ? *av_fetch(colour, 1, 0) : NULL;
80 181 50         b = len >= 2 ? *av_fetch(colour, 2, 0) : NULL;
81              
82 181 50         c.r = (r && SvOK(r)) ? SvNV(r) : 255;
    100          
83 181 50         c.g = (g && SvOK(g)) ? SvNV(g) : 255;
    100          
84 181 50         c.b = (b && SvOK(b)) ? SvNV(b) : 255;
    100          
85 181           c.a = SvNV(*hv_fetch((HV*)SvRV(self), "alpha", 5, 0));
86 181           return c;
87             }
88              
89             /* ── Convert a colour string into an AV ref of [r,g,b(,a)] ────── */
90              
91 10070           static SV * xs_convert_colour(const char * colour) {
92             dTHX;
93             colouring_rgba_t c;
94             AV * av;
95              
96 10070 100         if (!colouring_parse(colour, &c)) {
97 2           croak("Cannot convert the colour format");
98             return &PL_sv_undef;
99             }
100              
101 10067           av = newAV();
102 10067           av_push(av, newSVnv(c.r));
103 10067           av_push(av, newSVnv(c.g));
104 10067           av_push(av, newSVnv(c.b));
105 10067 100         if (c.a != 1.0) {
106 10           av_push(av, newSVnv(c.a));
107             }
108 10067           return newRV_noinc((SV*)av);
109             }
110              
111             /* ── Pack RGBA back into a blessed object ─────────────────────── */
112              
113 10154           static SV * xs_new_color(SV * class, SV * colour, SV * a) {
114             dTHX;
115 10154           HV * hash = newHV();
116 10154 100         if (SvTYPE(SvRV(colour)) == SVt_PVAV) {
117 84 100         if (av_len((AV*)SvRV(colour)) == 3) {
118 2           a = av_pop((AV*)SvRV(colour));
119             }
120 84           hv_store(hash, "colour", 6, newSVsv(colour), 0);
121             } else {
122 10070           colour = xs_convert_colour(SvPV_nolen(colour));
123 10067 100         if (av_len((AV*)SvRV(colour)) == 3) {
124 10           a = av_pop((AV*)SvRV(colour));
125             }
126 10067           hv_store(hash, "colour", 6, colour, 0);
127             }
128 10151 50         hv_store(hash, "alpha", 5, numIs(a) ? newSVsv(a) : newSViv(1), 0);
129 10151           return xs_new(class, hash);
130             }
131              
132             /* ── Build a new object from colouring_rgba_t ─────────────────── */
133              
134 70           static SV * xs_rgba_to_obj(SV * class, colouring_rgba_t c) {
135             dTHX;
136 70           AV * av = newAV();
137 70           av_push(av, newSVnv(c.r));
138 70           av_push(av, newSVnv(c.g));
139 70           av_push(av, newSVnv(c.b));
140 70           return xs_new_color(class, newRV_noinc((SV*)av), newSVnv(c.a));
141             }
142              
143             /* ── Ensure SV is a colour object (convert string -> obj) ─────── */
144              
145 70           static SV * xs_ensure_obj(SV * class, SV * colour) {
146             dTHX;
147 70 100         if (!SvROK(colour)) {
148 57           return xs_new_color(class, colour, newSVnv(1));
149             }
150 13           return colour;
151             }
152              
153             /* ── Convenience: class SV from constant ──────────────────────── */
154              
155 13           static SV * xs_class_sv(void) {
156             dTHX;
157 13           return newSVpv(COLOURING_CLASS, COLOURING_CLASS_LEN);
158             }
159              
160             #endif /* COLOURING_IN_H */