NXWidgets  1.19
cscaledbitmap.cxx
Go to the documentation of this file.
1 /****************************************************************************
2  * NxWidgets/libnxwidgets/src/cscaledbitmap.hxx
3  *
4  * Copyright (C) 2013-2014 Gregory Nutt. All rights reserved.
5  * Author: Gregory Nutt <gnutt@nuttx.org>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  * notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in
15  * the documentation and/or other materials provided with the
16  * distribution.
17  * 3. Neither the name NuttX, NxWidgets, nor the names of its contributors
18  * me be used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
28  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  ****************************************************************************/
35 
36 /****************************************************************************
37  * Included Files
38  ****************************************************************************/
39 
40 #include <nuttx/config.h>
41 
42 #include <stdint.h>
43 #include <stdbool.h>
44 #include <cstring>
45 
46 #include <nuttx/nx/nxglib.h>
47 
48 #include "cscaledbitmap.hxx"
49 
50 /****************************************************************************
51  * Pre-Processor Definitions
52  ****************************************************************************/
53 
54 /****************************************************************************
55  * Method Implementations
56  ****************************************************************************/
57 
58 using namespace NXWidgets;
59 
60 /**
61  * Constructor.
62  *
63  * @param bitmap The bitmap structure being scaled.
64  * @newSize The new, scaled size of the image
65  */
66 
67 CScaledBitmap::CScaledBitmap(IBitmap *bitmap, struct nxgl_size_s &newSize)
68 : m_bitmap(bitmap), m_size(newSize)
69 {
70  // xScale will be used to convert a request X position to an X position
71  // in the contained bitmap:
72  //
73  // xImage = xRequested * oldWidth / newWidth
74  // = xRequested * xScale
75 
76  m_xScale = itob16((uint32_t)m_bitmap->getWidth()) / newSize.w;
77 
78  // Similarly, yScale will be used to convert a request Y position to a Y
79  // positionin the contained bitmap:
80  //
81  // yImage = yRequested * oldHeight / newHeight
82  // = yRequested * yScale
83 
84  m_yScale = itob16((uint32_t)m_bitmap->getHeight()) / newSize.h;
85 
86  // Allocate and initialize the row cache
87 
88  size_t stride = bitmap->getStride();
89  m_rowCache[0] = new uint8_t[stride];
90  m_rowCache[1] = new uint8_t[stride];
91 
92  // Read the first two rows into the cache
93 
94  m_row = m_bitmap->getWidth(); // Set to an impossible value
95  cacheRows(0);
96 }
97 
98 /**
99  * Destructor.
100  */
101 
103 {
104  // Delete the allocated row cache memory
105 
106  if (m_rowCache[0])
107  {
108  delete m_rowCache[0];
109  }
110 
111  if (m_rowCache[1])
112  {
113  delete m_rowCache[1];
114  }
115 
116  // We are also responsible for deleting the contained IBitmap
117 
118  if (m_bitmap)
119  {
120  delete m_bitmap;
121  }
122 }
123 
124 /**
125  * Get the bitmap's color format.
126  *
127  * @return The bitmap's width.
128  */
129 
130 const uint8_t CScaledBitmap::getColorFormat(void) const
131 {
132  return m_bitmap->getColorFormat();
133 }
134 
135 /**
136  * Get the bitmap's color format.
137  *
138  * @return The bitmap's color format.
139  */
140 
141 const uint8_t CScaledBitmap::getBitsPerPixel(void) const
142 {
143  return m_bitmap->getBitsPerPixel();
144 }
145 
146 /**
147  * Get the bitmap's width (in pixels/columns).
148  *
149  * @return The bitmap's pixel depth.
150  */
151 
152 const nxgl_coord_t CScaledBitmap::getWidth(void) const
153 {
154  return m_size.w;
155 }
156 
157 /**
158  * Get the bitmap's height (in rows).
159  *
160  * @return The bitmap's height (in rows).
161  */
162 
163 const nxgl_coord_t CScaledBitmap::getHeight(void) const
164 {
165  return m_size.h;
166 }
167 
168 /**
169  * Get the bitmap's width (in bytes).
170  *
171  * @return The bitmap's width (in bytes).
172  */
173 
174 const size_t CScaledBitmap::getStride(void) const
175 {
176  return (m_bitmap->getBitsPerPixel() * m_size.w + 7) / 8;
177 }
178 
179 /**
180  * Get one row from the bit map image.
181  *
182  * REVISIT: This algorithm is really intended to expand images. Hence,
183  * for example, interpolation is between row and row+1 and column and
184  * column+1 in the original, unscaled image. You would the interpolation
185  * differently if you really wanted to sub-sample well.
186  *
187  * @param x The offset into the row to get
188  * @param y The row number to get
189  * @param width The number of pixels to get from the row
190  * @param data The memory location provided by the caller
191  * in which to return the data. This should be at least
192  * (getWidth()*getBitsPerPixl() + 7)/8 bytes in length
193  * and properly aligned for the pixel color format.
194  * @param True if the run was returned successfully.
195  */
196 
197 bool CScaledBitmap::getRun(nxgl_coord_t x, nxgl_coord_t y,
198  nxgl_coord_t width, FAR void *data)
199 {
200 #if CONFIG_NXWIDGETS_FMT == FB_FMT_RGB8_332 || CONFIG_NXWIDGETS_FMT == FB_FMT_RGB24
201  FAR uint8_t *dest = (FAR uint8_t *)data;
202 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB16_565
203  FAR uint16_t *dest = (FAR uint16_t *)data;
204 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB32
205  FAR uint32_t *dest = (FAR uint32_t *)data;
206 #else
207 # error Unsupported, invalid, or undefined color format
208 #endif
209 
210  // Check ranges. Casts to unsigned int are ugly but permit one-sided comparisons
211 
212  if (((unsigned int)x >= (unsigned int)m_size.w) &&
213  ((unsigned int)(x + width) > (unsigned int)m_size.w) &&
214  ((unsigned int)y <= (unsigned int)m_size.h))
215  {
216  return false;
217  }
218 
219  // Get the row number in the unscaled image corresponding to the
220  // requested y position. This must be either the exact row or the
221  // closest row just before the requested position
222 
223  b16_t row16 = y * m_yScale;
224  nxgl_coord_t row = b16toi(row16);
225 
226  // Get that row and the one after it into the row cache. We know that
227  // the pixel value that we want is one between the two rows. This
228  // may seem wasteful to read two entire rows. However, in normal usage
229  // we will be traversal each image from top-left to bottom-right in
230  // order. In that case, the caching is most efficient.
231 
232  if (!cacheRows(row))
233  {
234  return false;
235  }
236 
237  // Now scale and copy the data from the cached row data
238 
239  for (int i = 0; i < width; i++, x++)
240  {
241  // Get the column number in the unscaled row corresponding to the
242  // requested x position. This must be either the exact column or the
243  // closest column just before the requested position
244 
245  b16_t column = x * m_xScale;
246 
247  // Get the color at the position on the first row
248 
249  struct rgbcolor_s color1;
250  if (!rowColor(m_rowCache[0], column, color1))
251  {
252  gerr("ERROR: rowColor failed for the first row\n");
253  return false;
254  }
255 
256  // Get the color at the position on the first row
257 
258  struct rgbcolor_s color2;
259  if (!rowColor(m_rowCache[1], column, color2))
260  {
261  gerr("ERROR: rowColor failed for the second row\n");
262  return false;
263  }
264 
265  // Check for transparent colors
266 
267  bool transparent1;
268  bool transparent2;
269 
270 #if CONFIG_NXWIDGETS_FMT == FB_FMT_RGB8_332
271  uint8_t color = RGBTO8(color1.r, color1.g, color1.b);
272  transparent1 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
273 
274  color = RGBTO8(color2.r, color2.g, color2.b);
275  transparent2 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
276 
277 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB16_565
278  uint16_t color = RGBTO16(color1.r, color1.g, color1.b);
279  transparent1 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
280 
281  color = RGBTO16(color2.r, color2.g, color2.b);
282  transparent2 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
283 
284 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB24 || CONFIG_NXWIDGETS_FMT == FB_FMT_RGB32
285  uint32_t color = RGBTO24(color1.r, color1.g, color1.b);
286  transparent1 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
287 
288  color = RGBTO24(color2.r, color2.g, color2.b);
289  transparent2 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
290 
291 #else
292 # error Unsupported, invalid, or undefined color format
293 #endif
294 
295  // Is one of the colors transparent?
296 
297  struct rgbcolor_s scaledColor;
298  b16_t fraction b16frac(row16);
299 
300  if (transparent1 || transparent2)
301  {
302  // Yes.. don't interpolate within transparent regions or
303  // between transparent and opaque regions.
304 
305  // Get the color closest to the requested position
306 
307  if (fraction < b16HALF)
308  {
309  scaledColor.r = color1.r;
310  scaledColor.g = color1.g;
311  scaledColor.b = color1.b;
312  }
313  else
314  {
315  scaledColor.r = color2.r;
316  scaledColor.g = color2.g;
317  scaledColor.b = color2.b;
318  }
319  }
320  else
321  {
322  // No.. both colors are opaque
323 
324  if (!scaleColor(color1, color2, fraction, scaledColor))
325  {
326  return false;
327  }
328  }
329 
330  // Write the interpolated data to the user buffer
331 
332 #if CONFIG_NXWIDGETS_FMT == FB_FMT_RGB8_332
333  color = RGBTO8(scaledColor.r, scaledColor.g, scaledColor.b);
334  *dest++ = color;
335 
336 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB16_565
337  color = RGBTO16(scaledColor.r, scaledColor.g, scaledColor.b);
338  *dest++ = color;
339 
340 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB24
341  *dest++ = color2.b;
342  *dest++ = color2.r;
343  *dest++ = color2.g;
344 
345 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB32
346  color = RGBTO24(scaledColor.r, scaledColor.g, scaledColor.b);
347  *dest++ = color;
348 
349 #else
350 # error Unsupported, invalid, or undefined color format
351 #endif
352  }
353 
354  return true;
355 }
356 
357 /**
358  * Read two rows into the row cache
359  *
360  * @param row - The row number of the first row to cache
361  */
362 
363 bool CScaledBitmap::cacheRows(unsigned int row)
364 {
365  nxgl_coord_t bitmapWidth = m_bitmap->getWidth();
366  nxgl_coord_t bitmapHeight = m_bitmap->getHeight();
367 
368  // A common case is to advance by one row. In this case, we only
369  // need to read one row
370 
371  if (row == m_row + 1)
372  {
373  // Swap rows
374 
375  FAR uint8_t *saveRow = m_rowCache[0];
376  m_rowCache[0] = m_rowCache[1];
377  m_rowCache[1] = saveRow;
378 
379  // Save number of the first row that we have in the cache
380 
381  m_row = row;
382 
383  // Now read the new row into the second row cache buffer
384 
385  if (++row >= (unsigned int)bitmapHeight)
386  {
387  row = bitmapHeight - 1;
388  }
389 
390  if (!m_bitmap->getRun(0, row, bitmapWidth, m_rowCache[1]))
391  {
392  gerr("ERROR: Failed to read bitmap row %d\n", row);
393  return false;
394  }
395  }
396 
397  // Do we need to read two new rows? Or do we already have the
398  // request row in the cache?
399 
400  else if (row != m_row)
401  {
402  // Read the first row into the cache
403 
404  if (row >= (unsigned int)bitmapHeight)
405  {
406  row = bitmapHeight - 1;
407  }
408 
409  if (!m_bitmap->getRun(0, row, bitmapWidth, m_rowCache[0]))
410  {
411  gerr("ERROR: Failed to read bitmap row %d\n", row);
412  return false;
413  }
414 
415  // Save number of the first row that we have in the cache
416 
417  m_row = row;
418 
419  // Read the next row into the cache
420 
421  if (++row >= (unsigned int)bitmapHeight)
422  {
423  row = bitmapHeight - 1;
424  }
425 
426  if (!m_bitmap->getRun(0, row, bitmapWidth, m_rowCache[1]))
427  {
428  gerr("ERROR: Failed to read bitmap row %d\n", row);
429  return false;
430  }
431  }
432 
433  return true;
434 }
435 
436 /**
437  * Given an two RGB colors and a fractional value, return the scaled
438  * value between the two colors.
439  *
440  * @param incolor1 - The first color to be used
441  * @param incolor2 - The second color to be used
442  * @param fraction - The fractional value
443  * @param outcolor - The returned, scaled color
444  */
445 
446 bool CScaledBitmap::scaleColor(FAR const struct rgbcolor_s &incolor1,
447  FAR const struct rgbcolor_s &incolor2,
448  b16_t fraction, FAR struct rgbcolor_s &outcolor)
449 {
450  uint8_t component;
451  b16_t red;
452  b16_t green;
453  b16_t blue;
454 
455  // A fraction of < 0.5 would mean to use use mostly color1; a fraction
456  // greater than 0.5 would men to use mostly color2
457 
458  b16_t remainder = b16ONE - fraction;
459 
460  // Interpolate each color value (converting to b15)
461 
462  red = (b16_t)incolor1.r * remainder + (b16_t)incolor2.r * fraction;
463  green = (b16_t)incolor1.g * remainder + (b16_t)incolor2.g * fraction;
464  blue = (b16_t)incolor1.b * remainder + (b16_t)incolor2.b * fraction;
465 
466  // Return the integer, interpolated values, clipping to the range of
467  // uint8_t
468 
469  component = b16toi(red);
470  outcolor.r = component < 256 ? component : 255;
471 
472  component = b16toi(green);
473  outcolor.g = component < 256 ? component : 255;
474 
475  component = b16toi(blue);
476  outcolor.b = component < 256 ? component : 255;
477  return true;
478 }
479 
480 /**
481  * Given an image row and a non-integer column offset, return the
482  * interpolated RGB color value corresponding to that position
483  *
484  * @param row - The pointer to the row in the row cache to use
485  * @param column - The non-integer column offset
486  * @param outcolor - The returned, interpolated color
487  *
488  */
489 
490 bool CScaledBitmap::rowColor(FAR uint8_t *row, b16_t column,
491  FAR struct rgbcolor_s &outcolor)
492 {
493  // This is the col at or just before the pixel of interest
494 
495  nxgl_coord_t col1 = b16toi(column);
496  nxgl_coord_t col2 = col1 + 1;
497 
498  nxgl_coord_t bitmapWidth = m_bitmap->getWidth();
499  if (col2 >= bitmapWidth)
500  {
501  col2 = bitmapWidth - 1;
502  }
503 
504  b16_t fraction = b16frac(column);
505 
506  struct rgbcolor_s color1;
507  struct rgbcolor_s color2;
508 
509  bool transparent1;
510  bool transparent2;
511 
512 #if CONFIG_NXWIDGETS_FMT == FB_FMT_RGB8_332
513  uint8_t color = row[col1];
514  color1.r = RGB8RED(color);
515  color1.g = RGB8GREEN(color);
516  color1.b = RGB8BLUE(color);
517 
518  transparent1 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
519 
520  color = row[col2];
521  color2.r = RGB8RED(color);
522  color2.g = RGB8GREEN(color);
523  color2.b = RGB8BLUE(color);
524 
525  transparent2 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
526 
527 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB16_565
528  FAR uint16_t *row16 = (FAR uint16_t*)row;
529  uint16_t color = row16[col1];
530  color1.r = RGB16RED(color);
531  color1.g = RGB16GREEN(color);
532  color1.b = RGB16BLUE(color);
533 
534  transparent1 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
535 
536  color = row16[col2];
537  color2.r = RGB16RED(color);
538  color2.g = RGB16GREEN(color);
539  color2.b = RGB16BLUE(color);
540 
541  transparent2 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
542 
543 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB24
544  unsigned int ndx = 3*col1;
545  color1.r = row[ndx+2];
546  color1.g = row[ndx+1];
547  color1.b = row[ndx];
548 
549  uint32_t color = RGBTO24(color1.r, color1.g, color1.b);
550  transparent1 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
551 
552  ndx = 3*col2;
553  color2.r = row[ndx+2];
554  color2.g = row[ndx+1];
555  color2.b = row[ndx];
556 
557  color = RGBTO24(color2.r, color2.g, color2.b);
558  transparent2 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
559 
560 #elif CONFIG_NXWIDGETS_FMT == FB_FMT_RGB32
561  FAR uint32_t *row32 = (FAR uint32_t*)row;
562  uint32_t color = row32[col1];
563  color1.r = RGB24RED(color);
564  color1.g = RGB24GREEN(color);
565  color1.b = RGB24BLUE(color);
566 
567  transparent1 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
568 
569  color = row32[col2];
570  color2.r = RGB24RED(color);
571  color2.g = RGB24GREEN(color);
572  color2.b = RGB24BLUE(color);
573 
574  transparent2 = (color == CONFIG_NXWIDGETS_TRANSPARENT_COLOR);
575 
576 #else
577 # error Unsupported, invalid, or undefined color format
578 #endif
579 
580  // Is one of the colors transparent?
581 
582  if (transparent1 || transparent2)
583  {
584  // Yes.. don't interpolate within transparent regions or
585  // between transparent and opaque regions.
586 
587  // Return the color closest to the requested position
588  //
589  // A fraction of < 0.5 would mean to use use mostly color1; a fraction
590  // greater than 0.5 would men to use mostly color2
591 
592  if (fraction < b16HALF)
593  {
594  outcolor.r = color1.r;
595  outcolor.g = color1.b;
596  outcolor.g = color1.g;
597  }
598  else
599  {
600  outcolor.r = color2.r;
601  outcolor.g = color2.b;
602  outcolor.g = color2.g;
603  }
604 
605  return true;
606  }
607  else
608  {
609  // No.. both colors are opaque
610 
611  return scaleColor(color1, color2, fraction, outcolor);
612  }
613 }
virtual bool getRun(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, FAR void *data)=0
virtual const nxgl_coord_t getHeight(void) const =0
bool getRun(nxgl_coord_t x, nxgl_coord_t y, nxgl_coord_t width, FAR void *data)
const uint8_t getBitsPerPixel(void) const
bool cacheRows(unsigned int row)
const uint8_t getColorFormat(void) const
virtual const size_t getStride(void) const =0
FAR uint8_t * m_rowCache[2]
bool rowColor(FAR uint8_t *row, b16_t column, FAR struct rgbcolor_s &outcolor)
virtual const nxgl_coord_t getWidth(void) const =0
CScaledBitmap(const CScaledBitmap &bitmap)
const nxgl_coord_t getWidth(void) const
const size_t getStride(void) const
static const NXWidgets::SRlePaletteBitmapEntry bitmap[]
virtual const uint8_t getBitsPerPixel(void) const =0
virtual const uint8_t getColorFormat(void) const =0
const nxgl_coord_t getHeight(void) const
bool scaleColor(FAR const struct rgbcolor_s &incolor1, FAR const struct rgbcolor_s &incolor2, b16_t fraction, FAR struct rgbcolor_s &outcolor)
struct nxgl_size_s m_size