FreeRDP
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 
34 static 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 
78 int 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 
206 static 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 
247 static 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 
290 static 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 
336 static 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 
381 int 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 
419 rdtkNinePatch* 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 
431 void 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 
440 int 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 
508 int 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 }