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