Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Miscellaneous refactoring #12

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from

Conversation

Rangi42
Copy link
Contributor

@Rangi42 Rangi42 commented Nov 29, 2023

Found some things while implementing QOI.

data = ctxmalloc(context, sizeof *data * context -> source -> width * context -> source -> height);
plum_convert_colors(data, context -> source -> data, (size_t) context -> source -> width * context -> source -> height,
PLUM_COLOR_32, context -> source -> color_format);
size_t size = (size_t) context -> source -> width * context -> source -> height;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Factoring out a common (size_t) context -> source -> width * context -> source -> height expression, as is done almost everywhere else.

}
size_t rowsize = (size_t) context -> source -> width * 3, padding = 0;
if (rowsize & 3) {
padding = 4 - (rowsize & 3);
rowsize += padding;
}
unsigned char * out = append_output_node(context, rowsize * context -> source -> height);
unsigned char * output = append_output_node(context, rowsize * context -> source -> height);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

output is a much more common name for this kind of variable elsewhere in the codebase.

@@ -21,6 +21,26 @@ struct plum_rectangle * get_frame_boundaries (struct context * context, bool anc

void adjust_frame_boundaries (const struct plum_image * image, struct plum_rectangle * restrict boundaries) {
uint64_t empty_color = get_empty_color(image);
#define checkframe(type) do \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here the palette code was so similar to the 16/32/64 color code that I wanted to use the existing checkframe macro for the palette as well.

index += (size_t) boundaries[frame].height * image -> width; \
for (size_t remaining = (size_t) (image -> height - boundaries[frame].top - boundaries[frame].height) * image -> width; remaining; remaining --) \
if (notempty(type)) goto done ## type; \
continue; \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This continue is an alternative to a manually-checked bool adjust flag.

This control flow:

for (...) {
  bool adjust = true;
  if (various stuff) goto done;
  adjust = false;
  done:
  if (adjust) finish up;
}

was refactored to this:

for (...) {
  if (various stuff) goto done;
  continue;
  done:
  finish up;
}

} \
while (false)
#undef notempty
#define notempty(bits) (image -> data ## bits[index ++] != empty_color)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rgbds has already been converted to C++; next up, libplum, with templates and anonymous closures replacing all these macros. >:3

@@ -12,15 +12,58 @@ void generate_GIF_data (struct context * context) {
if ((uint8_t) (depth >> 8) > overall) overall = depth >> 8;
if ((uint8_t) (depth >> 16) > overall) overall = depth >> 16;
if (overall > 8) overall = 8;
header[4] = (overall - 1) << 4;
header[5] = header[6] = 0;
bytewrite(header + 4, (overall - 1) << 4, 0, 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using bytewrite for more than two values, same as elsewhere.

if (context -> source -> palette)
generate_GIF_data_with_palette(context, header);
else
generate_GIF_data_from_raw(context, header);
byteoutput(context, 0x3b);
}

#define checkboundaries(buffertype, buffer, frame) do { \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The generate_GIF_data_with_palette and generate_GIF_frame_data cases were very similar, so I factored them out into this macro.

(I'm sure there are even more such cases, but I found the ones that I did in this PR by inspecting the remaining gotos again. It's probably not a coincidence that the most complex goto logic is mostly found inside this sort of repetitive code.) (Non-complex gotos are the ones used for skipping to a failure case, or breaking out of nested loops/switches.)

top = boundaries[frame].top; \
width = boundaries[frame].width; \
height = boundaries[frame].height; \
} else { \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This else takes the place of an unnecessary goto.

This control flow:

if (boundaries) {
  if (various stuff) goto findbounds;
  we have the bounds;
  goto gotbounds;
}
findbounds:
more stuff;
gotbounds:

was refactored to this:

if (boundaries) {
  if (various stuff) goto findbounds;
  we have the bounds;
} else {
  findbounds:
  more stuff;
}

} \
} \
if (left || width != context -> source -> width) { \
buffertype * target = buffer; \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There'd be no need for buffertype in C23, just do typeof(buffer) target = buffer;.

@@ -46,7 +46,7 @@ static inline uint16_t bitextend16 (uint16_t value, unsigned width) {
static inline void * append_output_node (struct context * context, size_t size) {
struct data_node * node = ctxmalloc(context, sizeof *node + size);
*node = (struct data_node) {.size = size, .previous = context -> output, .next = NULL};
if (context -> output) context -> output -> next = node;
if (node -> previous) node -> previous -> next = node;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is more appropriate, because it expresses the general case of replacing a doubly-linked list node, without hard-coding the thing that this one's previous node happens to be. Also it matches the resize_output_node function in the QOI PR.

@@ -109,7 +109,7 @@ size_t compute_uncompressed_PNG_block_size (const unsigned char * restrict data,
if (length) {
score += length - 1;
if (score >= 16) break;
} else if (score > 0)
} else if (score)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

score is unsigned, and everywhere else you avoid doing unsigned > 0.

header[8] = (type < 4) ? 1 << type : (8 << (type >= 6));
header[9] = (type >= 4) ? 2 + 4 * (type & 1) : 3;
bytewrite(header + 10, 0, 0, 0);
bytewrite(header + 8, (type < 4) ? 1 << type : (8 << (type >= 6)), (type >= 4) ? 2 + 4 * (type & 1) : 3, 0, 0, 0);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using bytewrite for more than two values.

for (unsigned char * out = buffer; node; node = node -> next) {
memcpy(out, node -> data, node -> size);
out += node -> size;
for (unsigned char * output = buffer; node; node = node -> next) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only other out variable is a uint_fast32_t in bmpwrite.c, not any pointers to output buffers.

}
}

size_t get_total_output_size (struct context * context) {
size_t result = 0;
size_t output_size = 0;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same name it's assigned to above.

@Rangi42 Rangi42 changed the base branch from master to develop November 29, 2023 16:23
} else {
const uint32_t * sp = source;
convert(sp);
#define convert(frombits, tobits) do { \
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You already had the formatpair macro below, may as well use it here and save a couple of lines + make the to-from paired logic clearer.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant