[ee2f0beb] | 1 | /*
|
---|
| 2 | * Copyright (c) 2020 Jiri Svoboda
|
---|
| 3 | * All rights reserved.
|
---|
| 4 | *
|
---|
| 5 | * Redistribution and use in source and binary forms, with or without
|
---|
| 6 | * modification, are permitted provided that the following conditions
|
---|
| 7 | * are met:
|
---|
| 8 | *
|
---|
| 9 | * - Redistributions of source code must retain the above copyright
|
---|
| 10 | * notice, this list of conditions and the following disclaimer.
|
---|
| 11 | * - Redistributions in binary form must reproduce the above copyright
|
---|
| 12 | * notice, this list of conditions and the following disclaimer in the
|
---|
| 13 | * documentation and/or other materials provided with the distribution.
|
---|
| 14 | * - The name of the author may not be used to endorse or promote products
|
---|
| 15 | * derived from this software without specific prior written permission.
|
---|
| 16 | *
|
---|
| 17 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
---|
| 18 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
---|
| 19 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
---|
| 20 | * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
---|
| 21 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
---|
| 22 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
---|
| 23 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
---|
| 24 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
---|
| 25 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
---|
| 26 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
---|
| 27 | */
|
---|
| 28 |
|
---|
| 29 | /** @addtogroup libgfxfont
|
---|
| 30 | * @{
|
---|
| 31 | */
|
---|
| 32 | /**
|
---|
| 33 | * @file Glyph bitmap
|
---|
| 34 | */
|
---|
| 35 |
|
---|
| 36 | #include <errno.h>
|
---|
[d2100e2] | 37 | #include <gfx/bitmap.h>
|
---|
[c78a03d] | 38 | #include <gfx/coord.h>
|
---|
[ee2f0beb] | 39 | #include <gfx/glyph_bmp.h>
|
---|
[d2100e2] | 40 | #include <io/pixelmap.h>
|
---|
[ee2f0beb] | 41 | #include <stdlib.h>
|
---|
[d2100e2] | 42 | #include "../private/font.h"
|
---|
| 43 | #include "../private/glyph.h"
|
---|
[ee2f0beb] | 44 | #include "../private/glyph_bmp.h"
|
---|
| 45 |
|
---|
[c78a03d] | 46 | static errno_t gfx_glyph_bmp_extend(gfx_glyph_bmp_t *, gfx_coord2_t *);
|
---|
| 47 |
|
---|
| 48 | /** Open glyph bitmap for editing.
|
---|
| 49 | *
|
---|
| 50 | * @param glyph Glyph
|
---|
| 51 | * @param rbmp Place to store glyph bitmap
|
---|
| 52 | * @return EOK on success, ENOMEM if out of memory
|
---|
| 53 | */
|
---|
| 54 | errno_t gfx_glyph_bmp_open(gfx_glyph_t *glyph, gfx_glyph_bmp_t **rbmp)
|
---|
| 55 | {
|
---|
[d2100e2] | 56 | gfx_font_t *font = glyph->font;
|
---|
[c78a03d] | 57 | gfx_glyph_bmp_t *bmp;
|
---|
[d2100e2] | 58 | pixelmap_t pmap;
|
---|
| 59 | gfx_bitmap_alloc_t alloc;
|
---|
| 60 | gfx_coord_t x, y;
|
---|
| 61 | pixel_t pixel;
|
---|
| 62 | errno_t rc;
|
---|
[c78a03d] | 63 |
|
---|
| 64 | bmp = calloc(1, sizeof(gfx_glyph_bmp_t));
|
---|
| 65 | if (bmp == NULL)
|
---|
| 66 | return ENOMEM;
|
---|
| 67 |
|
---|
[313ac8e] | 68 | /* Bitmap coordinates are relative to glyph origin point */
|
---|
| 69 | gfx_rect_rtranslate(&glyph->origin, &glyph->rect, &bmp->rect);
|
---|
[d2100e2] | 70 |
|
---|
[313ac8e] | 71 | bmp->pixels = calloc((bmp->rect.p1.x - bmp->rect.p0.x) *
|
---|
| 72 | (bmp->rect.p1.y - bmp->rect.p0.y), sizeof(int));
|
---|
[d2100e2] | 73 | if (bmp->pixels == NULL) {
|
---|
| 74 | free(bmp);
|
---|
| 75 | return ENOMEM;
|
---|
| 76 | }
|
---|
| 77 |
|
---|
| 78 | rc = gfx_bitmap_get_alloc(font->bitmap, &alloc);
|
---|
| 79 | if (rc != EOK) {
|
---|
| 80 | free(bmp->pixels);
|
---|
| 81 | free(bmp);
|
---|
| 82 | return rc;
|
---|
| 83 | }
|
---|
| 84 |
|
---|
| 85 | assert(font->rect.p0.x == 0);
|
---|
| 86 | assert(font->rect.p0.y == 0);
|
---|
| 87 | pmap.width = font->rect.p1.x;
|
---|
| 88 | pmap.height = font->rect.p1.y;
|
---|
| 89 | pmap.data = alloc.pixels;
|
---|
| 90 |
|
---|
| 91 | /* Copy pixels from font bitmap */
|
---|
| 92 |
|
---|
[313ac8e] | 93 | for (y = bmp->rect.p0.y; y < bmp->rect.p1.y; y++) {
|
---|
| 94 | for (x = bmp->rect.p0.x; x < bmp->rect.p1.x; x++) {
|
---|
| 95 | pixel = pixelmap_get_pixel(&pmap, glyph->origin.x + x,
|
---|
| 96 | glyph->origin.y + y);
|
---|
| 97 | bmp->pixels[(y - bmp->rect.p0.y) *
|
---|
| 98 | (bmp->rect.p1.x - bmp->rect.p0.x) +
|
---|
| 99 | (x - bmp->rect.p0.x)] = (pixel != 0) ? 1 : 0;
|
---|
[d2100e2] | 100 | }
|
---|
| 101 | }
|
---|
[c78a03d] | 102 |
|
---|
| 103 | bmp->glyph = glyph;
|
---|
| 104 | *rbmp = bmp;
|
---|
| 105 | return EOK;
|
---|
| 106 | }
|
---|
| 107 |
|
---|
[ee2f0beb] | 108 | /** Save glyph bitmap.
|
---|
| 109 | *
|
---|
| 110 | * @param bmp Glyph bitmap
|
---|
| 111 | * @return EOK on success, ENOMEM if out of memory
|
---|
| 112 | */
|
---|
| 113 | errno_t gfx_glyph_bmp_save(gfx_glyph_bmp_t *bmp)
|
---|
| 114 | {
|
---|
[d2100e2] | 115 | gfx_glyph_t *glyph = bmp->glyph;
|
---|
| 116 | gfx_font_t *font = glyph->font;
|
---|
| 117 | gfx_coord_t x, y;
|
---|
[dd65f4f7] | 118 | gfx_rect_t used_rect;
|
---|
[d2100e2] | 119 | pixel_t pixel;
|
---|
| 120 | pixelmap_t pmap;
|
---|
| 121 | gfx_bitmap_alloc_t alloc;
|
---|
| 122 | errno_t rc;
|
---|
| 123 |
|
---|
[dd65f4f7] | 124 | /* Find actual rectangle being used */
|
---|
| 125 | gfx_glyph_bmp_find_used_rect(bmp, &used_rect);
|
---|
| 126 |
|
---|
[d2100e2] | 127 | /*
|
---|
| 128 | * Replace glyph with empty space in the font bitmap, the width
|
---|
| 129 | * of the empty equal to new glyph bitmap width. The glyph width
|
---|
| 130 | * is adjusted.
|
---|
| 131 | */
|
---|
[dd65f4f7] | 132 | rc = gfx_font_splice_at_glyph(font, glyph, &used_rect);
|
---|
[d2100e2] | 133 | if (rc != EOK)
|
---|
| 134 | return rc;
|
---|
| 135 |
|
---|
| 136 | rc = gfx_bitmap_get_alloc(font->bitmap, &alloc);
|
---|
| 137 | if (rc != EOK)
|
---|
| 138 | return rc;
|
---|
| 139 |
|
---|
| 140 | assert(font->rect.p0.x == 0);
|
---|
| 141 | assert(font->rect.p0.y == 0);
|
---|
| 142 | pmap.width = font->rect.p1.x;
|
---|
| 143 | pmap.height = font->rect.p1.y;
|
---|
| 144 | pmap.data = alloc.pixels;
|
---|
| 145 |
|
---|
| 146 | /* Copy pixels to font bitmap */
|
---|
| 147 |
|
---|
[dd65f4f7] | 148 | for (y = used_rect.p0.y; y < used_rect.p1.y; y++) {
|
---|
| 149 | for (x = used_rect.p0.x; x < used_rect.p1.x; x++) {
|
---|
[313ac8e] | 150 | pixel = bmp->pixels[(y - bmp->rect.p0.y) *
|
---|
| 151 | (bmp->rect.p1.x - bmp->rect.p0.x) +
|
---|
| 152 | (x - bmp->rect.p0.x)] ?
|
---|
[d2100e2] | 153 | PIXEL(255, 255, 255, 255) : PIXEL(0, 0, 0, 0);
|
---|
[313ac8e] | 154 | pixelmap_put_pixel(&pmap, glyph->origin.x + x,
|
---|
| 155 | glyph->origin.y + y, pixel);
|
---|
[d2100e2] | 156 | }
|
---|
| 157 | }
|
---|
| 158 |
|
---|
[ee2f0beb] | 159 | return EOK;
|
---|
| 160 | }
|
---|
| 161 |
|
---|
| 162 | /** Close glyph bitmap.
|
---|
| 163 | *
|
---|
| 164 | * @param bmp Glyph bitmap
|
---|
| 165 | */
|
---|
| 166 | void gfx_glyph_bmp_close(gfx_glyph_bmp_t *bmp)
|
---|
| 167 | {
|
---|
[c78a03d] | 168 | free(bmp->pixels);
|
---|
| 169 | free(bmp);
|
---|
[ee2f0beb] | 170 | }
|
---|
| 171 |
|
---|
[32066f2] | 172 | /** Get rectangle covered by glyph bitmap.
|
---|
| 173 | *
|
---|
| 174 | * @param bmp Glyph bitmap
|
---|
| 175 | * @param rect Place to store rectangle
|
---|
| 176 | */
|
---|
| 177 | void gfx_glyph_bmp_get_rect(gfx_glyph_bmp_t *bmp, gfx_rect_t *rect)
|
---|
| 178 | {
|
---|
| 179 | *rect = bmp->rect;
|
---|
| 180 | }
|
---|
| 181 |
|
---|
[dd65f4f7] | 182 | /** Find minimum rectangle covering all non-background pixels.
|
---|
| 183 | *
|
---|
| 184 | * @param bmp Glyph bitmap
|
---|
| 185 | * @param rect Place to store rectangle
|
---|
| 186 | */
|
---|
| 187 | void gfx_glyph_bmp_find_used_rect(gfx_glyph_bmp_t *bmp, gfx_rect_t *rect)
|
---|
| 188 | {
|
---|
| 189 | gfx_coord_t x, y;
|
---|
| 190 | gfx_coord2_t min;
|
---|
| 191 | gfx_coord2_t max;
|
---|
| 192 | bool anypix;
|
---|
| 193 | int pix;
|
---|
| 194 |
|
---|
| 195 | min.x = bmp->rect.p1.x;
|
---|
| 196 | min.y = bmp->rect.p1.y;
|
---|
| 197 | max.x = bmp->rect.p0.x;
|
---|
| 198 | max.y = bmp->rect.p0.y;
|
---|
| 199 |
|
---|
| 200 | anypix = false;
|
---|
| 201 | for (y = bmp->rect.p0.y; y < bmp->rect.p1.y; y++) {
|
---|
| 202 | for (x = bmp->rect.p0.x; x < bmp->rect.p1.x; x++) {
|
---|
| 203 | pix = gfx_glyph_bmp_getpix(bmp, x, y);
|
---|
| 204 | if (pix != 0) {
|
---|
| 205 | anypix = true;
|
---|
| 206 | if (x < min.x)
|
---|
| 207 | min.x = x;
|
---|
| 208 | if (y < min.y)
|
---|
| 209 | min.y = y;
|
---|
| 210 | if (x > max.x)
|
---|
| 211 | max.x = x;
|
---|
| 212 | if (y > max.y)
|
---|
| 213 | max.y = y;
|
---|
| 214 | }
|
---|
| 215 | }
|
---|
| 216 | }
|
---|
| 217 |
|
---|
| 218 | if (anypix) {
|
---|
| 219 | rect->p0.x = min.x;
|
---|
| 220 | rect->p0.y = min.y;
|
---|
| 221 | rect->p1.x = max.x + 1;
|
---|
| 222 | rect->p1.y = max.y + 1;
|
---|
| 223 | } else {
|
---|
| 224 | rect->p0.x = 0;
|
---|
| 225 | rect->p0.y = 0;
|
---|
| 226 | rect->p1.x = 0;
|
---|
| 227 | rect->p1.y = 0;
|
---|
| 228 | }
|
---|
| 229 | }
|
---|
| 230 |
|
---|
[ee2f0beb] | 231 | /** Get pixel from glyph bitmap.
|
---|
| 232 | *
|
---|
| 233 | * @param bmp Glyph bitmap
|
---|
| 234 | * @param x X-coordinate
|
---|
| 235 | * @param y Y-coordinate
|
---|
| 236 | * @return Pixel value
|
---|
| 237 | */
|
---|
| 238 | int gfx_glyph_bmp_getpix(gfx_glyph_bmp_t *bmp, gfx_coord_t x, gfx_coord_t y)
|
---|
| 239 | {
|
---|
[c78a03d] | 240 | gfx_coord2_t pos;
|
---|
| 241 | size_t pitch;
|
---|
| 242 |
|
---|
| 243 | pos.x = x;
|
---|
| 244 | pos.y = y;
|
---|
| 245 | if (!gfx_pix_inside_rect(&pos, &bmp->rect))
|
---|
| 246 | return 0;
|
---|
| 247 |
|
---|
| 248 | pitch = bmp->rect.p1.x - bmp->rect.p0.x;
|
---|
[313ac8e] | 249 |
|
---|
[c78a03d] | 250 | return bmp->pixels[(y - bmp->rect.p0.y) * pitch +
|
---|
| 251 | (x - bmp->rect.p0.x)];
|
---|
[ee2f0beb] | 252 | }
|
---|
| 253 |
|
---|
| 254 | /** Set pixel in glyph bitmap.
|
---|
| 255 | *
|
---|
| 256 | * @param bmp Glyph bitmap
|
---|
| 257 | * @param x X-coordinate
|
---|
| 258 | * @param y Y-coordinate
|
---|
| 259 | *
|
---|
| 260 | * @reutrn EOK on success, ENOMEM if out of memory
|
---|
| 261 | */
|
---|
| 262 | errno_t gfx_glyph_bmp_setpix(gfx_glyph_bmp_t *bmp, gfx_coord_t x,
|
---|
| 263 | gfx_coord_t y, int value)
|
---|
| 264 | {
|
---|
[c78a03d] | 265 | gfx_coord2_t pos;
|
---|
| 266 | size_t pitch;
|
---|
| 267 | errno_t rc;
|
---|
| 268 |
|
---|
| 269 | pos.x = x;
|
---|
| 270 | pos.y = y;
|
---|
[313ac8e] | 271 |
|
---|
[c78a03d] | 272 | if (!gfx_pix_inside_rect(&pos, &bmp->rect)) {
|
---|
| 273 | rc = gfx_glyph_bmp_extend(bmp, &pos);
|
---|
| 274 | if (rc != EOK)
|
---|
| 275 | return rc;
|
---|
| 276 | }
|
---|
| 277 |
|
---|
| 278 | pitch = bmp->rect.p1.x - bmp->rect.p0.x;
|
---|
| 279 | bmp->pixels[(y - bmp->rect.p0.y) * pitch +
|
---|
| 280 | (x - bmp->rect.p0.x)] = value;
|
---|
| 281 | return EOK;
|
---|
| 282 | }
|
---|
| 283 |
|
---|
[efca2e4] | 284 | /** Clear glyph bitmap
|
---|
| 285 | *
|
---|
| 286 | * @param bmp Glyph bitmap
|
---|
| 287 | *
|
---|
| 288 | * @return EOK on sucesss, ENOMEM if out of memory
|
---|
| 289 | */
|
---|
| 290 | errno_t gfx_glyph_bmp_clear(gfx_glyph_bmp_t *bmp)
|
---|
| 291 | {
|
---|
| 292 | int *npixels;
|
---|
| 293 |
|
---|
| 294 | /* Allocate new pixel array */
|
---|
[1fa6292] | 295 | npixels = calloc(1, sizeof(int));
|
---|
[efca2e4] | 296 | if (npixels == NULL)
|
---|
| 297 | return ENOMEM;
|
---|
| 298 |
|
---|
| 299 | /* Switch new and old data */
|
---|
| 300 | free(bmp->pixels);
|
---|
| 301 | bmp->pixels = npixels;
|
---|
| 302 | bmp->rect.p0.x = 0;
|
---|
| 303 | bmp->rect.p0.y = 0;
|
---|
| 304 | bmp->rect.p1.x = 0;
|
---|
| 305 | bmp->rect.p1.y = 0;
|
---|
| 306 |
|
---|
| 307 | return EOK;
|
---|
| 308 | }
|
---|
| 309 |
|
---|
[c78a03d] | 310 | /** Extend glyph bitmap to cover a patricular pixel.
|
---|
| 311 | *
|
---|
| 312 | * @param bmp Glyph bitmap
|
---|
| 313 | * @param pos Pixel position
|
---|
| 314 | *
|
---|
| 315 | * @return EOK on sucesss, ENOMEM if out of memory
|
---|
| 316 | */
|
---|
| 317 | static errno_t gfx_glyph_bmp_extend(gfx_glyph_bmp_t *bmp, gfx_coord2_t *pos)
|
---|
| 318 | {
|
---|
| 319 | gfx_rect_t prect;
|
---|
| 320 | gfx_rect_t nrect;
|
---|
| 321 | int *npixels;
|
---|
| 322 | size_t npitch;
|
---|
| 323 | size_t opitch;
|
---|
| 324 | gfx_coord_t x, y;
|
---|
| 325 |
|
---|
| 326 | /* Compute new rectangle enveloping current rectangle and new pixel */
|
---|
| 327 | prect.p0 = *pos;
|
---|
| 328 | prect.p1.x = prect.p0.x + 1;
|
---|
| 329 | prect.p1.y = prect.p0.y + 1;
|
---|
| 330 |
|
---|
| 331 | gfx_rect_envelope(&bmp->rect, &prect, &nrect);
|
---|
| 332 |
|
---|
| 333 | /* Allocate new pixel array */
|
---|
[1fa6292] | 334 | npixels = calloc((nrect.p1.x - nrect.p0.x) *
|
---|
| 335 | (nrect.p1.y - nrect.p0.y), sizeof(int));
|
---|
[c78a03d] | 336 | if (npixels == NULL)
|
---|
| 337 | return ENOMEM;
|
---|
| 338 |
|
---|
| 339 | /* Transfer pixel data */
|
---|
| 340 | opitch = bmp->rect.p1.x - bmp->rect.p0.x;
|
---|
| 341 | npitch = nrect.p1.x - nrect.p0.x;
|
---|
| 342 |
|
---|
| 343 | for (y = bmp->rect.p0.y; y < bmp->rect.p1.y; y++) {
|
---|
| 344 | for (x = bmp->rect.p0.x; x < bmp->rect.p1.x; x++) {
|
---|
| 345 | npixels[(y - nrect.p0.y) * npitch + x - nrect.p0.x] =
|
---|
| 346 | bmp->pixels[(y - bmp->rect.p0.y) * opitch +
|
---|
[5592c56] | 347 | x - bmp->rect.p0.x];
|
---|
[c78a03d] | 348 | }
|
---|
| 349 | }
|
---|
| 350 |
|
---|
| 351 | /* Switch new and old data */
|
---|
| 352 | free(bmp->pixels);
|
---|
| 353 | bmp->pixels = npixels;
|
---|
| 354 | bmp->rect = nrect;
|
---|
| 355 |
|
---|
[ee2f0beb] | 356 | return EOK;
|
---|
| 357 | }
|
---|
| 358 |
|
---|
| 359 | /** @}
|
---|
| 360 | */
|
---|