FreeRDP
Loading...
Searching...
No Matches
rdtk_nine_patch.c
1
19#include <winpr/config.h>
20#include <winpr/assert.h>
21
22#include <rdtk/config.h>
23
24#include "rdtk_resources.h"
25
26#include "rdtk_nine_patch.h"
27
28#if defined(WINPR_WITH_PNG)
29#define FILE_EXT "png"
30#else
31#define FILE_EXT "bmp"
32#endif
33
34static int rdtk_image_copy_alpha_blend(uint8_t* pDstData, int nDstStep, int nXDst, int nYDst,
35 int nWidth, int nHeight, const uint8_t* pSrcData,
36 int nSrcStep, int nXSrc, int nYSrc)
37{
38 WINPR_ASSERT(pDstData);
39 WINPR_ASSERT(pSrcData);
40
41 for (int y = 0; y < nHeight; y++)
42 {
43 const uint8_t* pSrcPixel = &pSrcData[((nYSrc + y) * nSrcStep) + (nXSrc * 4)];
44 uint8_t* pDstPixel = &pDstData[((nYDst + y) * nDstStep) + (nXDst * 4)];
45
46 for (int x = 0; x < nWidth; x++)
47 {
48 uint8_t B = pSrcPixel[0];
49 uint8_t G = pSrcPixel[1];
50 uint8_t R = pSrcPixel[2];
51 uint8_t A = pSrcPixel[3];
52 pSrcPixel += 4;
53
54 if (A == 255)
55 {
56 pDstPixel[0] = B;
57 pDstPixel[1] = G;
58 pDstPixel[2] = R;
59 }
60 else
61 {
62 R = (R * A) / 255;
63 G = (G * A) / 255;
64 B = (B * A) / 255;
65 pDstPixel[0] = B + (pDstPixel[0] * (255 - A) + (255 / 2)) / 255;
66 pDstPixel[1] = G + (pDstPixel[1] * (255 - A) + (255 / 2)) / 255;
67 pDstPixel[2] = R + (pDstPixel[2] * (255 - A) + (255 / 2)) / 255;
68 }
69
70 pDstPixel[3] = 0xFF;
71 pDstPixel += 4;
72 }
73 }
74
75 return 1;
76}
77
78int rdtk_nine_patch_draw(rdtkSurface* surface, int nXDst, int nYDst, int nWidth, int nHeight,
79 rdtkNinePatch* ninePatch)
80{
81 WINPR_ASSERT(surface);
82 WINPR_ASSERT(ninePatch);
83
84 if (nWidth < ninePatch->width)
85 nWidth = ninePatch->width;
86
87 if (nHeight < ninePatch->height)
88 nHeight = ninePatch->height;
89
90 WINPR_UNUSED(nHeight);
91
92 int scaleWidth = nWidth - (ninePatch->width - ninePatch->scaleWidth);
93 int nSrcStep = ninePatch->scanline;
94 const uint8_t* pSrcData = ninePatch->data;
95 uint8_t* pDstData = surface->data;
96 WINPR_ASSERT(surface->scanline <= INT_MAX);
97 int nDstStep = (int)surface->scanline;
98 /* top */
99 int x = 0;
100 int y = 0;
101 /* top left */
102 int nXSrc = 0;
103 int nYSrc = 0;
104 int width = ninePatch->scaleLeft;
105 int height = ninePatch->scaleTop;
106 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height, pSrcData,
107 nSrcStep, nXSrc, nYSrc);
108 x += width;
109 /* top middle (scalable) */
110 nXSrc = ninePatch->scaleLeft;
111 nYSrc = 0;
112 height = ninePatch->scaleTop;
113
114 while (x < (nXSrc + scaleWidth))
115 {
116 width = (nXSrc + scaleWidth) - x;
117
118 if (width > ninePatch->scaleWidth)
119 width = ninePatch->scaleWidth;
120
121 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height,
122 pSrcData, nSrcStep, nXSrc, nYSrc);
123 x += width;
124 }
125
126 /* top right */
127 nXSrc = ninePatch->scaleRight;
128 nYSrc = 0;
129 width = ninePatch->width - ninePatch->scaleRight;
130 height = ninePatch->scaleTop;
131 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height, pSrcData,
132 nSrcStep, nXSrc, nYSrc);
133 /* middle */
134 x = 0;
135 y = ninePatch->scaleTop;
136 /* middle left */
137 nXSrc = 0;
138 nYSrc = ninePatch->scaleTop;
139 width = ninePatch->scaleLeft;
140 height = ninePatch->scaleHeight;
141 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height, pSrcData,
142 nSrcStep, nXSrc, nYSrc);
143 x += width;
144 /* middle (scalable) */
145 nXSrc = ninePatch->scaleLeft;
146 nYSrc = ninePatch->scaleTop;
147 height = ninePatch->scaleHeight;
148
149 while (x < (nXSrc + scaleWidth))
150 {
151 width = (nXSrc + scaleWidth) - x;
152
153 if (width > ninePatch->scaleWidth)
154 width = ninePatch->scaleWidth;
155
156 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height,
157 pSrcData, nSrcStep, nXSrc, nYSrc);
158 x += width;
159 }
160
161 /* middle right */
162 nXSrc = ninePatch->scaleRight;
163 nYSrc = ninePatch->scaleTop;
164 width = ninePatch->width - ninePatch->scaleRight;
165 height = ninePatch->scaleHeight;
166 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height, pSrcData,
167 nSrcStep, nXSrc, nYSrc);
168 /* bottom */
169 x = 0;
170 y = ninePatch->scaleBottom;
171 /* bottom left */
172 nXSrc = 0;
173 nYSrc = ninePatch->scaleBottom;
174 width = ninePatch->scaleLeft;
175 height = ninePatch->height - ninePatch->scaleBottom;
176 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height, pSrcData,
177 nSrcStep, nXSrc, nYSrc);
178 x += width;
179 /* bottom middle (scalable) */
180 nXSrc = ninePatch->scaleLeft;
181 nYSrc = ninePatch->scaleBottom;
182 height = ninePatch->height - ninePatch->scaleBottom;
183
184 while (x < (nXSrc + scaleWidth))
185 {
186 width = (nXSrc + scaleWidth) - x;
187
188 if (width > ninePatch->scaleWidth)
189 width = ninePatch->scaleWidth;
190
191 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height,
192 pSrcData, nSrcStep, nXSrc, nYSrc);
193 x += width;
194 }
195
196 /* bottom right */
197 nXSrc = ninePatch->scaleRight;
198 nYSrc = ninePatch->scaleBottom;
199 width = ninePatch->width - ninePatch->scaleRight;
200 height = ninePatch->height - ninePatch->scaleBottom;
201 rdtk_image_copy_alpha_blend(pDstData, nDstStep, nXDst + x, nYDst + y, width, height, pSrcData,
202 nSrcStep, nXSrc, nYSrc);
203 return 1;
204}
205
206static BOOL rdtk_nine_patch_get_scale_lr(rdtkNinePatch* ninePatch, wImage* image)
207{
208 WINPR_ASSERT(image);
209 WINPR_ASSERT(ninePatch);
210
211 WINPR_ASSERT(image->data);
212 WINPR_ASSERT(image->width > 0);
213
214 int64_t beg = -1;
215 int64_t end = -1;
216
217 for (uint32_t x = 1; x < image->width - 1; x++)
218 {
219 const uint32_t* pixel = (const uint32_t*)&image->data[sizeof(uint32_t) * x]; /* (1, 0) */
220 if (beg < 0)
221 {
222 if (*pixel)
223 beg = x;
224 }
225 else if (end < 0)
226 {
227 if (!(*pixel))
228 {
229 end = x;
230 break;
231 }
232 }
233 }
234
235 if ((beg <= 0) || (end <= 0))
236 return FALSE;
237
238 WINPR_ASSERT(beg <= INT32_MAX);
239 WINPR_ASSERT(end <= INT32_MAX);
240 ninePatch->scaleLeft = (int32_t)beg - 1;
241 ninePatch->scaleRight = (int32_t)end - 1;
242 ninePatch->scaleWidth = ninePatch->scaleRight - ninePatch->scaleLeft;
243
244 return TRUE;
245}
246
247static BOOL rdtk_nine_patch_get_scale_ht(rdtkNinePatch* ninePatch, wImage* image)
248{
249 WINPR_ASSERT(image);
250 WINPR_ASSERT(ninePatch);
251
252 WINPR_ASSERT(image->data);
253 WINPR_ASSERT(image->height > 0);
254 WINPR_ASSERT(image->scanline > 0);
255
256 int64_t beg = -1;
257 int64_t end = -1;
258
259 for (uint32_t y = 1; y < image->height - 1; y++)
260 {
261 const uint32_t* pixel =
262 (const uint32_t*)&image->data[1ULL * image->scanline * y]; /* (1, 0) */
263 if (beg < 0)
264 {
265 if (*pixel)
266 beg = y;
267 }
268 else if (end < 0)
269 {
270 if (!(*pixel))
271 {
272 end = y;
273 break;
274 }
275 }
276 }
277
278 if ((beg <= 0) || (end <= 0))
279 return FALSE;
280
281 WINPR_ASSERT(beg <= INT32_MAX);
282 WINPR_ASSERT(end <= INT32_MAX);
283 ninePatch->scaleTop = (int32_t)beg - 1;
284 ninePatch->scaleBottom = (int32_t)end - 1;
285 ninePatch->scaleHeight = ninePatch->scaleBottom - ninePatch->scaleTop;
286
287 return TRUE;
288}
289
290static BOOL rdtk_nine_patch_get_fill_lr(rdtkNinePatch* ninePatch, wImage* image)
291{
292 WINPR_ASSERT(image);
293 WINPR_ASSERT(ninePatch);
294
295 WINPR_ASSERT(image->data);
296 WINPR_ASSERT(image->width > 0);
297 WINPR_ASSERT(image->height > 0);
298 WINPR_ASSERT(image->scanline > 0);
299
300 int64_t beg = -1;
301 int64_t end = -1;
302
303 for (uint32_t x = 1; x < image->width - 1; x++)
304 {
305 const uint32_t* pixel =
306 (uint32_t*)&image->data[((1ULL * image->height - 1ULL) * image->scanline) +
307 x * sizeof(uint32_t)]; /* (1, height - 1) */
308 if (beg < 0)
309 {
310 if (*pixel)
311 beg = x;
312 }
313 else if (end < 0)
314 {
315 if (!(*pixel))
316 {
317 end = x;
318 break;
319 }
320 }
321 }
322
323 if ((beg <= 0) || (end <= 0))
324 return FALSE;
325
326 WINPR_ASSERT(beg <= INT32_MAX);
327 WINPR_ASSERT(end <= INT32_MAX);
328
329 ninePatch->fillLeft = (int32_t)beg - 1;
330 ninePatch->fillRight = (int32_t)end - 1;
331 ninePatch->fillWidth = ninePatch->fillRight - ninePatch->fillLeft;
332
333 return TRUE;
334}
335
336static BOOL rdtk_nine_patch_get_fill_ht(rdtkNinePatch* ninePatch, wImage* image)
337{
338 WINPR_ASSERT(image);
339 WINPR_ASSERT(ninePatch);
340
341 WINPR_ASSERT(image->data);
342 WINPR_ASSERT(image->width > 0);
343 WINPR_ASSERT(image->height > 0);
344 WINPR_ASSERT(image->scanline > 0);
345
346 int64_t beg = -1;
347 int64_t end = -1;
348
349 for (uint32_t y = 1; y < image->height - 1; y++)
350 {
351 const uint32_t* pixel =
352 (uint32_t*)&image->data[((image->width - 1) * sizeof(uint32_t)) +
353 1ull * image->scanline * y]; /* (width - 1, 1) */
354 if (beg < 0)
355 {
356 if (*pixel)
357 beg = y;
358 }
359 else if (end < 0)
360 {
361 if (!(*pixel))
362 {
363 end = y;
364 break;
365 }
366 }
367 }
368
369 if ((beg <= 0) || (end <= 0))
370 return FALSE;
371
372 WINPR_ASSERT(beg <= INT32_MAX);
373 WINPR_ASSERT(end <= INT32_MAX);
374 ninePatch->scaleTop = (int32_t)beg - 1;
375 ninePatch->scaleBottom = (int32_t)end - 1;
376 ninePatch->scaleHeight = ninePatch->scaleBottom - ninePatch->scaleTop;
377
378 return TRUE;
379}
380
381int rdtk_nine_patch_set_image(rdtkNinePatch* ninePatch, wImage* image)
382{
383 WINPR_ASSERT(image);
384 WINPR_ASSERT(ninePatch);
385
386 ninePatch->image = image;
387
388 /* parse scalable area */
389 if (!rdtk_nine_patch_get_scale_lr(ninePatch, image))
390 return -1;
391
392 if (!rdtk_nine_patch_get_scale_ht(ninePatch, image))
393 return -1;
394
395 /* parse fillable area */
396 if (!rdtk_nine_patch_get_fill_lr(ninePatch, image))
397 return -1;
398
399 if (!rdtk_nine_patch_get_fill_ht(ninePatch, image))
400 return -1;
401
402 /* cut out borders from image */
403 WINPR_ASSERT(image->width >= 2);
404 WINPR_ASSERT(image->height >= 2);
405 WINPR_ASSERT(image->scanline > 0);
406 WINPR_ASSERT(image->width <= INT32_MAX);
407 WINPR_ASSERT(image->height <= INT32_MAX);
408 WINPR_ASSERT(image->scanline <= INT32_MAX);
409 WINPR_ASSERT(image->data);
410
411 ninePatch->width = (int32_t)image->width - 2;
412 ninePatch->height = (int32_t)image->height - 2;
413 ninePatch->data = &image->data[image->scanline + 4]; /* (1, 1) */
414 ninePatch->scanline = (int32_t)image->scanline;
415
416 return 1;
417}
418
419rdtkNinePatch* rdtk_nine_patch_new(rdtkEngine* engine)
420{
421 WINPR_ASSERT(engine);
422 rdtkNinePatch* ninePatch = (rdtkNinePatch*)calloc(1, sizeof(rdtkNinePatch));
423
424 if (!ninePatch)
425 return NULL;
426
427 ninePatch->engine = engine;
428 return ninePatch;
429}
430
431void rdtk_nine_patch_free(rdtkNinePatch* ninePatch)
432{
433 if (!ninePatch)
434 return;
435
436 winpr_image_free(ninePatch->image, TRUE);
437 free(ninePatch);
438}
439
440int rdtk_nine_patch_engine_init(rdtkEngine* engine)
441{
442 int status = 0;
443 wImage* image = NULL;
444 rdtkNinePatch* ninePatch = NULL;
445
446 WINPR_ASSERT(engine);
447
448 if (!engine->button9patch)
449 {
450 SSIZE_T size = 0;
451 const uint8_t* data = NULL;
452 status = -1;
453 size = rdtk_get_embedded_resource_file("btn_default_normal.9." FILE_EXT, &data);
454
455 if (size > 0)
456 {
457 image = winpr_image_new();
458
459 if (image)
460 status = winpr_image_read_buffer(image, data, (size_t)size);
461 }
462
463 if (status > 0)
464 {
465 ninePatch = engine->button9patch = rdtk_nine_patch_new(engine);
466
467 if (ninePatch)
468 rdtk_nine_patch_set_image(ninePatch, image);
469 else
470 winpr_image_free(image, TRUE);
471 }
472 else
473 winpr_image_free(image, TRUE);
474 }
475
476 if (!engine->textField9patch)
477 {
478 SSIZE_T size = 0;
479 const uint8_t* data = NULL;
480 status = -1;
481 size = rdtk_get_embedded_resource_file("textfield_default.9." FILE_EXT, &data);
482 image = NULL;
483
484 if (size > 0)
485 {
486 image = winpr_image_new();
487
488 if (image)
489 status = winpr_image_read_buffer(image, data, (size_t)size);
490 }
491
492 if (status > 0)
493 {
494 ninePatch = engine->textField9patch = rdtk_nine_patch_new(engine);
495
496 if (ninePatch)
497 rdtk_nine_patch_set_image(ninePatch, image);
498 else
499 winpr_image_free(image, TRUE);
500 }
501 else
502 winpr_image_free(image, TRUE);
503 }
504
505 return 1;
506}
507
508int rdtk_nine_patch_engine_uninit(rdtkEngine* engine)
509{
510 WINPR_ASSERT(engine);
511 if (engine->button9patch)
512 {
513 rdtk_nine_patch_free(engine->button9patch);
514 engine->button9patch = NULL;
515 }
516
517 if (engine->textField9patch)
518 {
519 rdtk_nine_patch_free(engine->textField9patch);
520 engine->textField9patch = NULL;
521 }
522
523 return 1;
524}