1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
|
/*
* File: webp.c
*
* Copyright (C) 2024 dogma
* Copyright (C) 2024 Rodrigo Arias Mallo <rodarima@gmail.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*/
#include <config.h>
#ifdef ENABLE_WEBP
#include <stdlib.h> /* For abort() */
#include <webp/decode.h>
#include "msg.h"
#include "image.hh"
#include "cache.h"
#include "dicache.h"
enum prog_state {
IS_finished, IS_init, IS_nextdata
};
typedef struct {
DilloImage *Image; /* Image meta data */
DilloUrl *url; /* Primary Key for the dicache */
int version; /* Secondary Key for the dicache */
int bgcolor; /* Parent widget background color */
int state;
int y;
WebPIDecoder* idec;
WebPDecBuffer output_buffer; /* for RGBA */
} DilloWebp;
/*
* Free up the resources for this image.
*/
static void Webp_free(DilloWebp *webp)
{
_MSG("Webp_free: webp=%p\n", webp);
WebPFreeDecBuffer(&webp->output_buffer);
if (webp->idec)
WebPIDelete(webp->idec);
dFree(webp);
}
/*
* Finish the decoding process (and free the memory)
*/
static void Webp_close(DilloWebp *webp, CacheClient_t *Client)
{
_MSG("Webp_close\n");
/* Let dicache know decoding is over */
a_Dicache_close(webp->url, webp->version, Client);
Webp_free(webp);
}
/*
* Op: Operation to perform.
* If (Op == 0)
* start or continue processing an image if image data exists.
* else
* terminate processing, cleanup any allocated memory,
* close down the decoding process.
*
* Client->CbData : pointer to previously allocated work area.
* This holds the current state of the image processing and is kept
* across calls to this routine.
* Client->Buf : Pointer to data start.
* Client->BufSize : the size of the data buffer.
*
* You have to keep track of where you are in the image data and
* how much has been processed.
*
* It's entirely possible that you will not see the end of the data. The
* user may terminate transfer via a Stop button or there may be a network
* failure. This means that you can't just wait for all the data to be
* presented before starting conversion and display.
*/
void a_Webp_callback(int Op, void *data)
{
CacheClient_t *Client = data;
if (Op == CA_Send) {
uint8_t* output;
VP8StatusCode ret;
DilloWebp *webp = (DilloWebp *)Client->CbData;
if (webp->state == IS_init) {
WebPBitstreamFeatures features;
ret = WebPGetFeatures(Client->Buf, Client->BufSize, &features);
if (ret != VP8_STATUS_OK) {
MSG("features ret is %d\n", ret);
return;
}
if (features.has_alpha) {
_MSG("WEBP: Alpha!\n");
}
webp->output_buffer.colorspace = features.has_alpha ? MODE_RGBA : MODE_RGB;
a_Dicache_set_parms(webp->url, webp->version, webp->Image,
features.width, features.height,
DILLO_IMG_TYPE_RGB, 1 / 2.2);
webp->idec = WebPINewDecoder(&webp->output_buffer);
webp->state = IS_nextdata;
}
ret = WebPIUpdate(webp->idec, Client->Buf, Client->BufSize);
/* SUSPENDED is a success state that means you don't have the entire file yet */
if (ret == VP8_STATUS_SUSPENDED || ret == VP8_STATUS_OK) {
/* last_y seems 1-based, which would be kind of crazy, but I would expect
* crazy idiocy from google.
*/
int last_y, width, height, stride;
_MSG("webp completed. status: %s\n", ret == VP8_STATUS_SUSPENDED ? "suspended" : "ok (done)");
output = WebPIDecGetRGB(webp->idec, &last_y, &width, &height, &stride);
if (!output) {
_MSG("webp decoding no output\n");
} else {
unsigned char *line;
int row = webp->y;
if (webp->output_buffer.colorspace == MODE_RGBA)
line = dNew(unsigned char, width * 3);
else
line = output + row * stride;
for (; row < last_y; row++) {
if (webp->output_buffer.colorspace == MODE_RGBA) {
int j;
uint_t bg_blue = (webp->bgcolor) & 0xFF;
uint_t bg_green = (webp->bgcolor>>8) & 0xFF;
uint_t bg_red = (webp->bgcolor>>16) & 0xFF;
for (j = 0; j < width; j++) {
uchar_t alpha = output[row * stride + 4 * j + 3];
uchar_t r = output[row * stride + 4 * j];
uchar_t g = output[row * stride + 4 * j + 1];
uchar_t b = output[row * stride + 4 * j + 2];
line[3 * j] = (r * alpha + (bg_red * (0xFF - alpha))) / 0xFF;
line[3 * j + 1] = (g * alpha + (bg_green * (0xFF - alpha))) / 0xFF;
line[3 * j + 2] = (b * alpha + (bg_blue * (0xFF - alpha))) / 0xFF;
}
} else {
line = output + row * stride;
}
a_Dicache_write(webp->url, webp->version, line, row);
}
webp->y = last_y;
if (webp->output_buffer.colorspace == MODE_RGBA)
dFree(line);
}
} else {
MSG("webp WebPIUpdate failed with %d\n", ret);
}
} else if (Op == CA_Close) {
Webp_close(Client->CbData, Client);
} else if (Op == CA_Abort) {
Webp_free(data);
}
}
const char *a_Webp_version(char *buf, int n)
{
/* Return the decoder's version number, packed in
* hexadecimal using 8bits for each of major/minor/revision.
* E.g: v2.5.7 is 0x020507. */
int ver = WebPGetDecoderVersion();
int major = (ver >> 16) & 0xff;
int minor = (ver >> 8) & 0xff;
int rev = (ver >> 0) & 0xff;
int k = snprintf(buf, n, "%d.%d.%d", major, minor, rev);
if (k >= n)
return "?";
return buf;
}
/*
* Create the image state data that must be kept between calls
*/
void *a_Webp_new(DilloImage *Image, DilloUrl *url, int version)
{
DilloWebp *webp = dNew0(DilloWebp, 1);
_MSG("a_Webp_new: webp=%p\n", webp);
webp->Image = Image;
webp->url = url;
webp->version = version;
webp->bgcolor = Image->bg_color;
webp->state = IS_init;
webp->y = 0;
webp->idec = NULL;
WebPInitDecBuffer(&webp->output_buffer);
return webp;
}
#else /* ENABLE_WEBP */
void *a_Webp_new() { return 0; }
void a_Webp_callback() { return; }
const char *a_Webp_version(char *buf, int n) { return 0; }
#endif /* ENABLE_WEBP */
|