ObjectivelyMVC
Object oriented MVC framework for SDL3 and GNU C
Loading...
Searching...
No Matches
ImageView.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 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgment in the product documentation would be
16 * appreciated but is not required.
17 *
18 * 2. Altered source versions must be plainly marked as such, and must not be
19 * misrepresented as being the original software.
20 *
21 * 3. This notice may not be removed or altered from any source distribution.
22 */
23
24#include <assert.h>
25
26#include "ImageAtlas.h"
27#include "ImageView.h"
28#include "Theme.h"
29
30const EnumName SDLGPUBlendFactorNames[] = MakeEnumNames(
31 MakeEnumName(SDL_GPU_BLENDFACTOR_ZERO),
32 MakeEnumName(SDL_GPU_BLENDFACTOR_ONE),
33 MakeEnumName(SDL_GPU_BLENDFACTOR_SRC_COLOR),
34 MakeEnumName(SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_COLOR),
35 MakeEnumName(SDL_GPU_BLENDFACTOR_DST_COLOR),
36 MakeEnumName(SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_COLOR),
37 MakeEnumName(SDL_GPU_BLENDFACTOR_SRC_ALPHA),
38 MakeEnumName(SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA),
39 MakeEnumName(SDL_GPU_BLENDFACTOR_DST_ALPHA),
40 MakeEnumName(SDL_GPU_BLENDFACTOR_ONE_MINUS_DST_ALPHA),
41 MakeEnumName(SDL_GPU_BLENDFACTOR_CONSTANT_COLOR),
42 MakeEnumName(SDL_GPU_BLENDFACTOR_ONE_MINUS_CONSTANT_COLOR),
43 MakeEnumName(SDL_GPU_BLENDFACTOR_SRC_ALPHA_SATURATE)
44);
45
46#define _Class _ImageView
47
48#pragma mark - Object
49
53static void dealloc(Object *self) {
54
55 ImageView *this = (ImageView *) self;
56
57 release(this->image);
58
59 free(this->imageName);
60
61 this->texture = release(this->texture);
62
63 super(Object, self, dealloc);
64}
65
66#pragma mark - View
67
71static void awakeWithDictionary(View *self, const Dictionary *dictionary) {
72
73 super(View, self, awakeWithDictionary, dictionary);
74
75 ImageView *this = (ImageView *) self;
76
77 const Inlet inlets[] = MakeInlets(
78 MakeInlet("blend.src", InletTypeEnum, &this->blend.src, (ident) SDLGPUBlendFactorNames),
79 MakeInlet("blend.dst", InletTypeEnum, &this->blend.dst, (ident) SDLGPUBlendFactorNames),
80 MakeInlet("color", InletTypeColor, &this->color, NULL),
81 MakeInlet("image", InletTypeCharacters, &this->imageName, NULL)
82 );
83
84 $(self, bind, inlets, dictionary);
85}
86
90static void didMoveToWindow(View *self, SDL_Window *window) {
91
92 super(View, self, didMoveToWindow, window);
93
94 ImageView *this = (ImageView *) self;
95
96 // leaving a window resolves nothing: there is no density to resolve against, and View::dealloc
97 // moves to a NULL window, where loading an image would be worse than pointless
98 if (window == NULL) {
99 return;
100 }
101
102 if (this->imageName == NULL) {
103 return;
104 }
105
106 char *name = this->imageName;
107 this->imageName = NULL;
108
109 Theme *theme = $$(Theme, theme, window);
110 if (theme) {
111 Image *image = $(theme, image, name, SDL_GetWindowPixelDensity(window));
112 $(this, setImage, image);
113 } else {
114 Image *image = $$(Image, imageWithResourceName, name, SDL_GetWindowPixelDensity(window));
115 $(this, setImage, image);
116 release(image);
117 }
118
119 this->imageName = name;
120}
121
125static View *init(View *self) {
126 return (View *) $((ImageView *) self, initWithImage, NULL);
127}
128
132static void render(View *self, Renderer *renderer) {
133
134 super(View, self, render, renderer);
135
136 ImageView *this = (ImageView *) self;
137
138 Texture *texture = NULL;
139 SDL_Rect region, *src = NULL;
140
141 if (this->image) {
142 if ($((Object *) this->image, isKindOfClass, _AtlasImage())) {
143
144 // The atlas owns the Texture, and repacks on compile, so neither is cached here
145 AtlasImage *atlasImage = (AtlasImage *) this->image;
146 if (atlasImage->atlas) {
147 texture = $(atlasImage->atlas, texture, renderer->device);
148 region = atlasImage->rect;
149 src = &region;
150 }
151
152 } else {
153 if (this->texture == NULL) {
154 this->texture = $(renderer->device, createTextureFromSurface,
155 this->image->surface, SDL_GPU_TEXTUREUSAGE_SAMPLER, false);
156 assert(this->texture);
157 }
158 texture = this->texture;
159 }
160 } else {
161 texture = this->texture;
162 }
163
164 if (texture) {
165
166 // TODO: Actually use self->blend
167
168 const SDL_Rect frame = $(self, renderFrame);
169
170 SDL_FRect frect;
171 SDL_RectToFRect(&frame, &frect);
172
173 if (self->borderRadius > 0) {
174 $(renderer, drawRoundedTextureRegion, texture, src, &frect, self->borderRadius, &this->color);
175 } else {
176 $(renderer, drawTextureRegion, texture, src, &frect, &this->color);
177 }
178 }
179}
180
184static void renderDeviceWillReset(View *self) {
185
186 ImageView *this = (ImageView *) self;
187
188 this->texture = release(this->texture);
189
190 if (this->image && $((Object *) this->image, isKindOfClass, _AtlasImage())) {
191 ImageAtlas *atlas = ((AtlasImage *) this->image)->atlas;
192 if (atlas) {
193 $(atlas, renderDeviceWillReset);
194 }
195 }
196
197 super(View, self, renderDeviceWillReset);
198}
199
200#pragma mark - ImageView
201
206static ImageView *initWithFrame(ImageView *self, const SDL_Rect *frame) {
207
208 self = (ImageView *) super(View, self, initWithFrame, frame);
209 if (self) {
210 self->blend.src = SDL_GPU_BLENDFACTOR_SRC_ALPHA;
211 self->blend.dst = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA;
212
213 self->color = Colors.White;
214 }
215
216 return self;
217}
218
224
225 self = (ImageView *) $(self, initWithFrame, NULL);
226 if (self) {
227 $(self, setImage, image);
228 }
229
230 return self;
231}
232
237static void setImage(ImageView *self, Image *image) {
238
239 free(self->imageName);
240 self->imageName = NULL;
241
242 release(self->image);
243
244 if (image) {
245 self->image = retain(image);
246
247 const SDL_Size size = $((View *) self, size);
248 const SDL_Size imageSize = $(image, size);
249
250 if (size.w == 0 && size.h == 0) {
251 $((View *) self, resize, &imageSize);
252 }
253 } else {
254 self->image = NULL;
255 }
256
257 self->texture = release(self->texture);
258}
259
264static void setImageWithResource(ImageView *self, const Resource *resource) {
265
266 Image *image = $$(Image, imageWithResource, resource, 1.f);
267
268 $(self, setImage, image);
269
270 release(image);
271}
272
277static void setImageWithResourceName(ImageView *self, const char *name) {
278
279 $(self, setImage, NULL);
280
281 // an empty name is no image, not a request to ask the resource system for ""
282 if (name && *name) {
283 self->imageName = strdup(name);
284
285 View *this = (View *) self;
286 if (this->window) {
287 $(this, didMoveToWindow, this->window);
288 }
289 }
290}
291
296static void setImageWithSurface(ImageView *self, SDL_Surface *surface) {
297
298 $(self, setImage, NULL);
299
300 if (surface) {
301 Image *image = $(alloc(Image), initWithSurface, surface);
302 assert(image);
303
304 $(self, setImage, image);
305
306 release(image);
307 }
308}
309
310#pragma mark - Class lifecycle
311
315static void initialize(Class *clazz) {
316
317 ((ObjectInterface *) clazz->interface)->dealloc = dealloc;
318
319 ((ViewInterface *) clazz->interface)->awakeWithDictionary = awakeWithDictionary;
320 ((ViewInterface *) clazz->interface)->didMoveToWindow = didMoveToWindow;
321 ((ViewInterface *) clazz->interface)->init = init;
322 ((ViewInterface *) clazz->interface)->render = render;
323 ((ViewInterface *) clazz->interface)->renderDeviceWillReset = renderDeviceWillReset;
324
325 ((ImageViewInterface *) clazz->interface)->initWithFrame = initWithFrame;
326 ((ImageViewInterface *) clazz->interface)->initWithImage = initWithImage;
327 ((ImageViewInterface *) clazz->interface)->setImage = setImage;
328 ((ImageViewInterface *) clazz->interface)->setImageWithResource = setImageWithResource;
329 ((ImageViewInterface *) clazz->interface)->setImageWithResourceName = setImageWithResourceName;
330 ((ImageViewInterface *) clazz->interface)->setImageWithSurface = setImageWithSurface;
331}
332
337Class *_ImageView(void) {
338 static Class *clazz;
339 static Once once;
340
341 do_once(&once, {
342 clazz = _initialize(&(const ClassDef) {
343 .name = "ImageView",
344 .superclass = _View(),
345 .instanceSize = sizeof(ImageView),
346 .interfaceSize = sizeof(ImageViewInterface),
347 .initialize = initialize,
348 });
349 });
350
351 return clazz;
352}
353
354#undef _Class
Class * _AtlasImage(void)
Definition AtlasImage.c:84
static String * name(const Font *self)
Definition Font.c:303
static Image * initWithSurface(Image *self, SDL_Surface *surface)
Definition Image.c:391
static Image * imageWithResource(const Resource *resource, float pixelDensity)
Definition Image.c:66
static Image * imageWithResourceName(const char *name, float pixelDensity)
Definition Image.c:74
static SDL_Size size(const Image *self)
Definition Image.c:416
static Texture * texture(ImageAtlas *self, RenderDevice *device)
Definition ImageAtlas.c:289
Packs many Images into one sheet, backed by one Texture.
static void setImageWithResource(ImageView *self, const Resource *resource)
Definition ImageView.c:264
static void didMoveToWindow(View *self, SDL_Window *window)
Definition ImageView.c:90
static void setImageWithSurface(ImageView *self, SDL_Surface *surface)
Definition ImageView.c:296
static View * init(View *self)
Definition ImageView.c:125
static void awakeWithDictionary(View *self, const Dictionary *dictionary)
Definition ImageView.c:71
static ImageView * initWithFrame(ImageView *self, const SDL_Rect *frame)
Definition ImageView.c:206
static void setImageWithResourceName(ImageView *self, const char *name)
Definition ImageView.c:277
static void setImage(ImageView *self, Image *image)
Definition ImageView.c:237
static void render(View *self, Renderer *renderer)
Definition ImageView.c:132
static ImageView * initWithImage(ImageView *self, Image *image)
Definition ImageView.c:223
static void dealloc(Object *self)
Definition ImageView.c:53
const EnumName SDLGPUBlendFactorNames[]
Definition ImageView.c:30
static void initialize(Class *clazz)
Definition ImageView.c:315
static void renderDeviceWillReset(View *self)
Definition ImageView.c:184
Class * _ImageView(void)
Definition ImageView.c:337
ImageViews render an Image in the context of a View hierarchy.
static void drawTextureRegion(const Renderer *self, Texture *texture, const SDL_Rect *src, const SDL_FRect *dest, const SDL_Color *color)
Definition Renderer.c:497
static void drawRoundedTextureRegion(const Renderer *self, Texture *texture, const SDL_Rect *src, const SDL_FRect *dest, int radius, const SDL_Color *color)
Definition Renderer.c:473
static Theme * theme(SDL_Window *window)
Definition Theme.c:295
static Image * image(Theme *self, const char *name, float pixelDensity)
Definition Theme.c:82
The Theme type.
@ InletTypeEnum
Definition View+JSON.h:75
@ InletTypeCharacters
Definition View+JSON.h:52
@ InletTypeColor
Definition View+JSON.h:62
#define MakeInlets(...)
Creates a null-termianted array of Inlets.
Definition View+JSON.h:215
#define MakeInlet(name, type, dest, data)
Creates an Inlet with the specified parameters.
Definition View+JSON.h:210
static void resize(View *self, const SDL_Size *size)
Definition View.c:1708
Class * _View(void)
Definition View.c:2336
static SDL_Rect renderFrame(const View *self)
Definition View.c:1629
An Image packed into an ImageAtlas.
Definition AtlasImage.h:41
SDL_Rect rect
This AtlasImage's rectangle within the atlas sheet, in texels.
Definition AtlasImage.h:66
ImageAtlas * atlas
The ImageAtlas this AtlasImage belongs to, or NULL once that atlas has been released,...
Definition AtlasImage.h:59
W3C Color constants.
Definition Colors.h:37
SDL_Color White
Definition Colors.h:185
Packs many Images into one sheet, backed by one Texture.
Definition ImageAtlas.h:49
Image loading.
Definition Image.h:41
ImageViews render an Image in the context of a View hierarchy.
Definition ImageView.h:45
Image * image
The image.
Definition ImageView.h:73
void setImage(ImageView *self, Image *image)
Sets the Image for this ImageView.
Definition ImageView.c:237
SDL_GPUBlendFactor src
Definition ImageView.h:62
Texture * texture
The GPU texture.
Definition ImageView.h:89
ImageView * initWithImage(ImageView *self, const SDL_Rect *frame)
SDL_GPUBlendFactor dst
Definition ImageView.h:62
SDL_Color color
The drawing color.
Definition ImageView.h:68
struct ImageView::@0 blend
The blend factors (currently unused; reserved for future per-pipeline support).
Inlets enable inbound data binding of View attributes through JSON.
Definition View+JSON.h:149
Renderer extends Object with ObjectivelyMVC's UI rendering layer.
Definition Renderer.h:75
RenderDevice * device
The backing RenderDevice.
Definition Renderer.h:97
The Theme type.
Definition Theme.h:54
Views are the fundamental building blocks of ObjectivelyMVC user interfaces.
Definition View.h:225
int borderRadius
The corner radius applied to the background, border and bevel.
Definition View.h:281
void render(View *self, Renderer *renderer)
Renders this View using the given renderer.
Definition Control.c:181