ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Image.c
Go to the documentation of this file.
1/*
2 * ObjectivelyMVC: Object oriented MVC framework for SDL3 and C.
3 * Copyright (C) 2014 Jay Dolan <jay@jaydolan.com>
4 * This software is provided 'as-is', without any express or implied
5 * warranty. In no event will the authors be held liable for any damages
6 * arising from the use of this software.
7 * Permission is granted to anyone to use this software for any purpose,
8 * including commercial applications, and to alter it and redistribute it
9 * freely, subject to the following restrictions:
10 * 1. The origin of this software must not be misrepresented; you must not
11 * claim that you wrote the original software. If you use this software
12 * in a product, an acknowledgment in the product documentation would be
13 * appreciated but is not required.
14 * 2. Altered source versions must be plainly marked as such, and must not be
15 * misrepresented as being the original software.
16 */
17
18#include <assert.h>
19#include <string.h>
20#include <stdio.h>
21
22#include <SDL3_image/SDL_image.h>
23
24#include "Image.h"
25#include "Log.h"
26
27#define _Class _Image
28
29
30#pragma mark - Object
31
35static void dealloc(Object *self) {
36
37 Image *this = (Image *) self;
38
39 SDL_DestroySurface(this->surface);
40
41 super(Object, self, dealloc);
42}
43
44#pragma mark - Image
45
50static Image *imageWithBytes(const uint8_t *bytes, size_t length, float pixelDensity) {
51 return $(alloc(Image), initWithBytes, bytes, length, pixelDensity);
52}
53
58static Image *imageWithData(const Data *data, float pixelDensity) {
59 return $(alloc(Image), initWithData, data, pixelDensity);
60}
61
66static Image *imageWithResource(const Resource *resource, float pixelDensity) {
67 return $(alloc(Image), initWithResource, resource, pixelDensity);
68}
69
74static Image *imageWithResourceName(const char *name, float pixelDensity) {
75 return $(alloc(Image), initWithResourceName, name, pixelDensity);
76}
77
82static Image *imageWithSurface(SDL_Surface *surface) {
83 return $(alloc(Image), initWithSurface, surface);
84}
85
90static Image *imageWithSVG(const uint8_t *bytes, size_t length, float scale) {
91 return $(alloc(Image), initWithSVG, bytes, length, scale);
92}
93
97static bool isSVG(const Image *self, const uint8_t *bytes, size_t length) {
98
99 if (self->type && SDL_strcasecmp(self->type, "svg") == 0) {
100 return true;
101 }
102
103 bool svg = false;
104
105 SDL_IOStream *stream = SDL_IOFromConstMem(bytes, (int) length);
106 if (stream) {
107 svg = IMG_isSVG(stream);
108 SDL_CloseIO(stream);
109 }
110
111 return svg;
112}
113
118static Image *initWithBytes(Image *self, const uint8_t *bytes, size_t length, float pixelDensity) {
119
120 if (isSVG(self, bytes, length)) {
121 return $(self, initWithSVG, bytes, length, pixelDensity);
122 }
123
124 SDL_IOStream *stream = SDL_IOFromConstMem(bytes, (int) length);
125 if (stream) {
126 SDL_Surface *surface = IMG_LoadTyped_IO(stream, 0, self->type);
127 if (surface) {
128 self = $(self, initWithSurface, surface);
129 SDL_DestroySurface(surface);
130 } else {
131 self = release(self);
132 }
133 SDL_CloseIO(stream);
134 } else {
135 self = release(self);
136 }
137
138 return self;
139}
140
145static Image *initWithData(Image *self, const Data *data, float pixelDensity) {
146
147 if (data) {
148 self = $(self, initWithBytes, data->bytes, data->length, pixelDensity);
149 } else {
150 self = release(self);
151 }
152
153 return self;
154}
155
160static Image *initWithResource(Image *self, const Resource *resource, float pixelDensity) {
161
162 if (resource) {
163 self->type = strrchr(resource->name, '.') ? strrchr(resource->name, '.') + 1 : NULL;
164 self = $(self, initWithData, resource->data, pixelDensity);
165 } else {
166 self = release(self);
167 }
168
169 return self;
170}
171
183static void parseDecoration(char *name, SDL_Size *points, float *scale) {
184
185 *points = MakeSize(0, 0);
186 *scale = 0.f;
187
188 char *at = strrchr(name, '@');
189 if (at == NULL) {
190 return;
191 }
192
193 char excess;
194 int w, h;
195
196 const char *extension = strrchr(name, '.');
197
198 if (extension && at > extension) {
199
200 if (sscanf(at + 1, "%dx%d%c", &w, &h, &excess) == 2 && w > 0 && h > 0) {
201 *points = MakeSize(w, h);
202 *at = '\0';
203 }
204
205 return;
206 }
207
208 const size_t length = (extension ? (size_t) (extension - at) : strlen(at)) - 1;
209
210 char decoration[32];
211 if (length == 0 || length >= sizeof(decoration)) {
212 return;
213 }
214
215 memcpy(decoration, at + 1, length);
216 decoration[length] = '\0';
217
218 if (sscanf(decoration, "%dx%c", &w, &excess) == 1 && w > 0) {
219 *scale = (float) w;
220 }
221}
222
227static char *variantName(const char *name, int density) {
228
229 const char *extension = strrchr(name, '.');
230 const int stem = extension ? (int) (extension - name) : (int) strlen(name);
231
232 char *variant = NULL;
233 if (SDL_asprintf(&variant, "%.*s@%dx%s", stem, name, density, extension ?: "") < 0) {
234 return NULL;
235 }
236
237 return variant;
238}
239
244static Image *initWithResourceName(Image *self, const char *name, float pixelDensity) {
245
246 assert(name);
247
248 char *resourceName = strdup(name);
249 assert(resourceName);
250
251 SDL_Size points;
252 float scale;
253
254 parseDecoration(resourceName, &points, &scale);
255
256 Resource *resource = NULL;
257
258 const char *extension = strrchr(resourceName, '.');
259
260 // an undecorated raster name prefers the variant matching the display, as @2x assets are
261 // chosen; a vector needs no variant, rasterizing to the density in initWithBytes instead
262 if (points.w == 0 && scale == 0.f && !(extension && SDL_strcasecmp(extension, ".svg") == 0)) {
263
264 const int density = (int) SDL_roundf(pixelDensity);
265 if (density > 1) {
266
267 char *variant = variantName(resourceName, density);
268 if (variant) {
269
270 resource = $$(Resource, resourceWithName, variant);
271 if (resource) {
272 scale = (float) density;
273 }
274
275 free(variant);
276 }
277 }
278 }
279
280 if (resource == NULL) {
281 resource = $$(Resource, resourceWithName, resourceName);
282 }
283
284 free(resourceName);
285
286 if (resource == NULL) {
287 return release(self);
288 }
289
290 if (points.w && points.h && resource->data &&
291 isSVG(self, resource->data->bytes, resource->data->length)) {
292 self = $(self, initWithSVGSize, resource->data->bytes, resource->data->length, points,
293 pixelDensity);
294 } else {
295 self = $(self, initWithResource, resource, pixelDensity);
296
297 if (self && scale > 0.f) {
298 self->scale = scale;
299 }
300 }
301
302 release(resource);
303
304 return self;
305}
306
310static SDL_Surface *rasterizeSVG(const uint8_t *bytes, size_t length, SDL_Size size) {
311
312 SDL_Surface *surface = NULL;
313
314 SDL_IOStream *stream = SDL_IOFromConstMem(bytes, (int) length);
315 if (stream) {
316 surface = IMG_LoadSizedSVG_IO(stream, size.w, size.h);
317 SDL_CloseIO(stream);
318 }
319
320 return surface;
321}
322
327static Image *initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale) {
328
329 scale = scale > 0.f ? scale : 1.f;
330
331 SDL_Surface *surface = rasterizeSVG(bytes, length, MakeSize(0, 0));
332
333 if (surface && scale != 1.f) {
334 const SDL_Size size = MakeSize(
335 (int) SDL_roundf(surface->w * scale),
336 (int) SDL_roundf(surface->h * scale)
337 );
338
339 SDL_DestroySurface(surface);
340 surface = rasterizeSVG(bytes, length, size);
341 }
342
343 if (surface) {
344 self = $(self, initWithSurface, surface);
345 SDL_DestroySurface(surface);
346
347 if (self) {
348 self->type = "svg";
349 self->scale = scale;
350 }
351 } else {
352 MVC_LogWarn("%s\n", SDL_GetError());
353 self = release(self);
354 }
355
356 return self;
357}
358
363static Image *initWithSVGSize(Image *self, const uint8_t *bytes, size_t length, SDL_Size points, float pixelDensity) {
364
365 const SDL_Size pixels = MakeSize(
366 (int) SDL_roundf(points.w * pixelDensity),
367 (int) SDL_roundf(points.h * pixelDensity)
368 );
369
370 SDL_Surface *surface = rasterizeSVG(bytes, length, pixels);
371 if (surface) {
372 self = $(self, initWithSurface, surface);
373 SDL_DestroySurface(surface);
374
375 if (self) {
376 self->type = "svg";
377 self->scale = pixelDensity;
378 }
379 } else {
380 MVC_LogWarn("%s\n", SDL_GetError());
381 self = release(self);
382 }
383
384 return self;
385}
386
391static Image *initWithSurface(Image *self, SDL_Surface *surface) {
392
393 self = (Image *) super(Object, self, init);
394 if (self) {
395 self->scale = 1.f;
396
397 if (surface) {
398 if (surface->format != SDL_PIXELFORMAT_RGBA32) {
399 self->surface = SDL_ConvertSurface(surface, SDL_PIXELFORMAT_RGBA32);
400 } else {
401 self->surface = surface;
402 self->surface->refcount++;
403 }
404
405 assert(self->surface);
406 }
407 }
408
409 return self;
410}
411
416static SDL_Size size(const Image *self) {
417 return MakeSize(
418 (int) SDL_roundf(self->surface->w / self->scale),
419 (int) SDL_roundf(self->surface->h / self->scale)
420 );
421}
422
423#pragma mark - Class lifecycle
424
428static void initialize(Class *clazz) {
429
430 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
431
432 ((ImageInterface *) clazz->interface)->imageWithBytes = imageWithBytes;
433 ((ImageInterface *) clazz->interface)->imageWithData = imageWithData;
434 ((ImageInterface *) clazz->interface)->imageWithResource = imageWithResource;
435 ((ImageInterface *) clazz->interface)->imageWithResourceName = imageWithResourceName;
436 ((ImageInterface *) clazz->interface)->imageWithSurface = imageWithSurface;
437 ((ImageInterface *) clazz->interface)->imageWithSVG = imageWithSVG;
438 ((ImageInterface *) clazz->interface)->initWithBytes = initWithBytes;
439 ((ImageInterface *) clazz->interface)->initWithData = initWithData;
440 ((ImageInterface *) clazz->interface)->initWithResource = initWithResource;
441 ((ImageInterface *) clazz->interface)->initWithResourceName = initWithResourceName;
442 ((ImageInterface *) clazz->interface)->initWithSurface = initWithSurface;
443 ((ImageInterface *) clazz->interface)->initWithSVG = initWithSVG;
444 ((ImageInterface *) clazz->interface)->initWithSVGSize = initWithSVGSize;
445 ((ImageInterface *) clazz->interface)->size = size;
446}
447
452Class *_Image(void) {
453 static Class *clazz;
454 static Once once;
455
456 do_once(&once, {
457 clazz = _initialize(&(const ClassDef) {
458 .name = "Image",
459 .superclass = _Object(),
460 .instanceSize = sizeof(Image),
461 .interfaceSize = sizeof(ImageInterface),
462 .initialize = initialize,
463 });
464 });
465
466 return clazz;
467}
468
469#undef _Class
static View * init(View *self)
Definition Box.c:67
static String * name(const Font *self)
Definition Font.c:303
static Image * imageWithBytes(const uint8_t *bytes, size_t length, float pixelDensity)
Definition Image.c:50
static Image * initWithSurface(Image *self, SDL_Surface *surface)
Definition Image.c:391
static Image * initWithResource(Image *self, const Resource *resource, float pixelDensity)
Definition Image.c:160
static Image * initWithBytes(Image *self, const uint8_t *bytes, size_t length, float pixelDensity)
Definition Image.c:118
static Image * imageWithResource(const Resource *resource, float pixelDensity)
Definition Image.c:66
static Image * imageWithSurface(SDL_Surface *surface)
Definition Image.c:82
static Image * initWithSVGSize(Image *self, const uint8_t *bytes, size_t length, SDL_Size points, float pixelDensity)
Definition Image.c:363
static Image * initWithData(Image *self, const Data *data, float pixelDensity)
Definition Image.c:145
Class * _Image(void)
Definition Image.c:452
static bool isSVG(const Image *self, const uint8_t *bytes, size_t length)
Definition Image.c:97
static Image * imageWithResourceName(const char *name, float pixelDensity)
Definition Image.c:74
static void dealloc(Object *self)
Definition Image.c:35
static SDL_Size size(const Image *self)
Definition Image.c:416
static Image * imageWithData(const Data *data, float pixelDensity)
Definition Image.c:58
static void initialize(Class *clazz)
Definition Image.c:428
static SDL_Surface * rasterizeSVG(const uint8_t *bytes, size_t length, SDL_Size size)
Rasterizes bytes as SVG at size pixels, or at its intrinsic size when size is zero.
Definition Image.c:310
static Image * initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale)
Definition Image.c:327
static char * variantName(const char *name, int density)
Composes the name of the @<density>x variant of name, e.g. foo@2x.png.
Definition Image.c:227
static void parseDecoration(char *name, SDL_Size *points, float *scale)
Interprets an @ decoration in name.
Definition Image.c:183
static Image * imageWithSVG(const uint8_t *bytes, size_t length, float scale)
Definition Image.c:90
static Image * initWithResourceName(Image *self, const char *name, float pixelDensity)
Definition Image.c:244
Image loading.
View logging facilities via SDL_Log.
#define MVC_LogWarn(fmt,...)
Definition Log.h:54
Image loading.
Definition Image.h:41
SDL_Size size(const Image *self)
Definition Image.c:416
const char * type
The image type, inferred if instantiated with a Resource.
Definition Image.h:67
Image * initWithData(Image *self, const Data *data)
Initializes this Image with the specified Data.
Image * initWithResourceName(Image *self, const char *name)
Initializes this Image, loading the Resource by the given name.
Image * initWithSVGSize(Image *self, const uint8_t *bytes, size_t length, SDL_Size points)
Initializes this Image by rasterizing the specified SVG at points times the pixel density,...
Image * initWithBytes(Image *self, const uint8_t *bytes, size_t length)
Initializes this Image with the specified bytes.
float scale
Pixels of surface per point: 1 for raster images, the requested scale for SVG.
Definition Image.h:57
Image * imageWithSVG(const uint8_t *bytes, size_t length, float scale)
Instantiates an Image by rasterizing the specified SVG.
Definition Image.c:90
Image * initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale)
Initializes this Image by rasterizing the specified SVG at its intrinsic size times scale.
Definition Image.c:327
Image * imageWithResourceName(const char *name)
Instantiates an Image with the specified Resource name.
Image * imageWithData(const Data *data)
Instantiates an Image with the specified Data.
Image * initWithSurface(Image *self, SDL_Surface *surface)
Initializes this Image with the given surface.
Definition Image.c:391
SDL_Surface * surface
The backing surface.
Definition Image.h:62
Image * imageWithResource(const Resource *resource)
Instantiates an Image with the specified Resource.
Image * imageWithSurface(SDL_Surface *surface)
Instantiates an Image with the specified surface.
Definition Image.c:82
Image * initWithResource(Image *self, const Resource *resource)
Initializes this Image with the specified Resource.