ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
Image.c File Reference
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <SDL3_image/SDL_image.h>
#include "Image.h"
#include "Log.h"

Go to the source code of this file.

Macros

#define _Class   _Image
 

Functions

Class * _Image (void)
 
static void dealloc (Object *self)
 
static Image * imageWithBytes (const uint8_t *bytes, size_t length, float pixelDensity)
 
static Image * imageWithData (const Data *data, float pixelDensity)
 
static Image * imageWithResource (const Resource *resource, float pixelDensity)
 
static Image * imageWithResourceName (const char *name, float pixelDensity)
 
static Image * imageWithSurface (SDL_Surface *surface)
 
static Image * imageWithSVG (const uint8_t *bytes, size_t length, float scale)
 
static void initialize (Class *clazz)
 
static Image * initWithBytes (Image *self, const uint8_t *bytes, size_t length, float pixelDensity)
 
static Image * initWithData (Image *self, const Data *data, float pixelDensity)
 
static Image * initWithResource (Image *self, const Resource *resource, float pixelDensity)
 
static Image * initWithResourceName (Image *self, const char *name, float pixelDensity)
 
static Image * initWithSurface (Image *self, SDL_Surface *surface)
 
static Image * initWithSVG (Image *self, const uint8_t *bytes, size_t length, float scale)
 
static Image * initWithSVGSize (Image *self, const uint8_t *bytes, size_t length, SDL_Size points, float pixelDensity)
 
static bool isSVG (const Image *self, const uint8_t *bytes, size_t length)
 
static void parseDecoration (char *name, SDL_Size *points, float *scale)
 Interprets an @ decoration in name.
 
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.
 
static SDL_Size size (const Image *self)
 
static char * variantName (const char *name, int density)
 Composes the name of the @<density>x variant of name, e.g. foo@2x.png.
 

Macro Definition Documentation

◆ _Class

#define _Class   _Image

Definition at line 27 of file Image.c.

Function Documentation

◆ _Image()

Class * _Image ( void  )

Definition at line 452 of file Image.c.

452 {
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}
static void initialize(Class *clazz)
Definition Image.c:428
Image loading.
Definition Image.h:41

◆ dealloc()

static void dealloc ( Object *  self)
static
See also
Object::dealloc(Object *)

Definition at line 35 of file Image.c.

35 {
36
37 Image *this = (Image *) self;
38
39 SDL_DestroySurface(this->surface);
40
41 super(Object, self, dealloc);
42}
static void dealloc(Object *self)
Definition Image.c:35

◆ imageWithBytes()

static Image * imageWithBytes ( const uint8_t *  bytes,
size_t  length,
float  pixelDensity 
)
static

Definition at line 50 of file Image.c.

50 {
51 return $(alloc(Image), initWithBytes, bytes, length, pixelDensity);
52}
static Image * initWithBytes(Image *self, const uint8_t *bytes, size_t length, float pixelDensity)
Definition Image.c:118

◆ imageWithData()

static Image * imageWithData ( const Data *  data,
float  pixelDensity 
)
static

Definition at line 58 of file Image.c.

58 {
59 return $(alloc(Image), initWithData, data, pixelDensity);
60}
static Image * initWithData(Image *self, const Data *data, float pixelDensity)
Definition Image.c:145

◆ imageWithResource()

static Image * imageWithResource ( const Resource *  resource,
float  pixelDensity 
)
static

Definition at line 66 of file Image.c.

66 {
67 return $(alloc(Image), initWithResource, resource, pixelDensity);
68}
static Image * initWithResource(Image *self, const Resource *resource, float pixelDensity)
Definition Image.c:160

◆ imageWithResourceName()

static Image * imageWithResourceName ( const char *  name,
float  pixelDensity 
)
static

Definition at line 74 of file Image.c.

74 {
75 return $(alloc(Image), initWithResourceName, name, pixelDensity);
76}
static String * name(const Font *self)
Definition Font.c:303
static Image * initWithResourceName(Image *self, const char *name, float pixelDensity)
Definition Image.c:244

◆ imageWithSurface()

static Image * imageWithSurface ( SDL_Surface *  surface)
static

Definition at line 82 of file Image.c.

82 {
83 return $(alloc(Image), initWithSurface, surface);
84}
static Image * initWithSurface(Image *self, SDL_Surface *surface)
Definition Image.c:391

◆ imageWithSVG()

static Image * imageWithSVG ( const uint8_t *  bytes,
size_t  length,
float  scale 
)
static

Definition at line 90 of file Image.c.

90 {
91 return $(alloc(Image), initWithSVG, bytes, length, scale);
92}
static Image * initWithSVG(Image *self, const uint8_t *bytes, size_t length, float scale)
Definition Image.c:327

◆ initialize()

static void initialize ( Class *  clazz)
static
See also
Class::initialize(Class *)

Definition at line 428 of file Image.c.

428 {
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}
static Image * imageWithBytes(const uint8_t *bytes, size_t length, float pixelDensity)
Definition Image.c:50
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 * imageWithResourceName(const char *name, float pixelDensity)
Definition Image.c:74
static SDL_Size size(const Image *self)
Definition Image.c:416
static Image * imageWithData(const Data *data, float pixelDensity)
Definition Image.c:58
static Image * imageWithSVG(const uint8_t *bytes, size_t length, float scale)
Definition Image.c:90
SDL_Size size(const Image *self)
Definition Image.c:416
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.
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
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.

◆ initWithBytes()

static Image * initWithBytes ( Image *  self,
const uint8_t *  bytes,
size_t  length,
float  pixelDensity 
)
static

Definition at line 118 of file Image.c.

118 {
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}
static bool isSVG(const Image *self, const uint8_t *bytes, size_t length)
Definition Image.c:97
const char * type
The image type, inferred if instantiated with a Resource.
Definition Image.h:67

◆ initWithData()

static Image * initWithData ( Image *  self,
const Data *  data,
float  pixelDensity 
)
static

Definition at line 145 of file Image.c.

145 {
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}

◆ initWithResource()

static Image * initWithResource ( Image *  self,
const Resource *  resource,
float  pixelDensity 
)
static

Definition at line 160 of file Image.c.

160 {
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}

◆ initWithResourceName()

static Image * initWithResourceName ( Image *  self,
const char *  name,
float  pixelDensity 
)
static

Definition at line 244 of file Image.c.

244 {
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}
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
float scale
Pixels of surface per point: 1 for raster images, the requested scale for SVG.
Definition Image.h:57

◆ initWithSurface()

static Image * initWithSurface ( Image *  self,
SDL_Surface *  surface 
)
static

Definition at line 391 of file Image.c.

391 {
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}
static View * init(View *self)
Definition Box.c:67
SDL_Surface * surface
The backing surface.
Definition Image.h:62

◆ initWithSVG()

static Image * initWithSVG ( Image *  self,
const uint8_t *  bytes,
size_t  length,
float  scale 
)
static

Definition at line 327 of file Image.c.

327 {
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}
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
#define MVC_LogWarn(fmt,...)
Definition Log.h:54

◆ initWithSVGSize()

static Image * initWithSVGSize ( Image *  self,
const uint8_t *  bytes,
size_t  length,
SDL_Size  points,
float  pixelDensity 
)
static

Definition at line 363 of file Image.c.

363 {
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}

◆ isSVG()

static bool isSVG ( const Image *  self,
const uint8_t *  bytes,
size_t  length 
)
static
Returns
True if bytes are an SVG document, by declared type or by sniffing.

Definition at line 97 of file Image.c.

97 {
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}

◆ parseDecoration()

static void parseDecoration ( char *  name,
SDL_Size *  points,
float *  scale 
)
static

Interprets an @ decoration in name.

Where the decoration sits says what it is. name.svg@<width>x<height> trails the whole reference and requests a size in points: it is a rasterization request, so it is stripped, and name.svg is what loads. name@<scale>x.ext sits inside the name, before the extension, and declares how many pixels per point a raster carries, following Apple; a file is named that way on disk, so it is left alone and loaded verbatim.

Parameters
nameThe resource name, edited in place.
pointsReceives the requested size, or zero.
scaleReceives the declared pixels per point, or zero.

Definition at line 183 of file Image.c.

183 {
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}

◆ rasterizeSVG()

static SDL_Surface * rasterizeSVG ( const uint8_t *  bytes,
size_t  length,
SDL_Size  size 
)
static

Rasterizes bytes as SVG at size pixels, or at its intrinsic size when size is zero.

Definition at line 310 of file Image.c.

310 {
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}

◆ size()

static SDL_Size size ( const Image *  self)
static

Definition at line 416 of file Image.c.

416 {
417 return MakeSize(
418 (int) SDL_roundf(self->surface->w / self->scale),
419 (int) SDL_roundf(self->surface->h / self->scale)
420 );
421}

◆ variantName()

static char * variantName ( const char *  name,
int  density 
)
static

Composes the name of the @<density>x variant of name, e.g. foo@2x.png.

Returns
The variant name, to be freed by the caller, or NULL on error.

Definition at line 227 of file Image.c.

227 {
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}