diff --git a/cucul/cucul.h b/cucul/cucul.h index d853ce7..8fb6354 100644 --- a/cucul/cucul.h +++ b/cucul/cucul.h @@ -122,7 +122,9 @@ int cucul_flip(cucul_canvas_t *); int cucul_flop(cucul_canvas_t *); int cucul_rotate_180(cucul_canvas_t *); int cucul_rotate_left(cucul_canvas_t *); +int cucul_rotate_left_wide(cucul_canvas_t *); int cucul_rotate_right(cucul_canvas_t *); +int cucul_rotate_right_wide(cucul_canvas_t *); /* @} */ /** \defgroup attributes libcucul attribute conversions diff --git a/cucul/transform.c b/cucul/transform.c index 0311b1b..262af20 100644 --- a/cucul/transform.c +++ b/cucul/transform.c @@ -303,6 +303,106 @@ int cucul_rotate_left(cucul_canvas_t *cv) return 0; } +/** \brief Rotate a canvas, 90 degrees counterclockwise (widechar version). + * + * Apply a 90-degree transformation to a canvas, choosing characters + * that look like the rotated version wherever possible. Characters cells + * are rotated two-by-two. Some characters will stay unchanged by the + * process, some others will be replaced by close equivalents. Fullwidth + * characters at odd horizontal coordinates will be lost. The operation is + * not guaranteed to be reversible at all. + * + * Note that the width of the canvas is divided by two and becomes the + * new height. Height is multiplied by two and becomes the new width. It + * is illegal to pass a canvas with an odd width to this function. + * + * If an error occurs, -1 is returned and \b errno is set accordingly: + * - \c EBUSY The canvas is in use by a display driver and cannot be rotated. + * - \c EINVAL The canvas' width is odd. + * - \c ENOMEM Not enough memory to allocate the new canvas size. If this + * happens, the previous canvas handle is still valid. + * + * \param cv The canvas to rotate left. + * \return 0 in case of success, -1 if an error occurred. + */ +int cucul_rotate_left_wide(cucul_canvas_t *cv) +{ + uint32_t *newchars, *newattrs; + unsigned int x, y, subwidth, subheight; + + if(cv->refcount) + { + seterrno(EBUSY); + return -1; + } + + if(cv->width & 1) + { + seterrno(EINVAL); + return -1; + } + + /* Save the current frame shortcuts */ + _cucul_save_frame_info(cv); + + newchars = malloc(cv->width * cv->height * sizeof(uint32_t)); + if(!newchars) + return -1; + + newattrs = malloc(cv->width * cv->height * sizeof(uint32_t)); + if(!newattrs) + { + free(newchars); + return -1; + } + + subwidth = cv->width / 2; + subheight = cv->height; + + for(y = 0; y < subheight; y++) + { + for(x = 0; x < subwidth; x++) + { + uint32_t ch1, ch2, attr1, attr2; + + ch1 = cv->chars[(subwidth * y + x) * 2]; + attr1 = cv->attrs[(subwidth * y + x) * 2]; + ch2 = cv->chars[(subwidth * y + x) * 2 + 1]; + attr2 = cv->attrs[(subwidth * y + x) * 2 + 1]; + + newchars[(subheight * (subwidth - 1 - x) + y) * 2] = ch1; + newattrs[(subheight * (subwidth - 1 - x) + y) * 2] = attr1; + newchars[(subheight * (subwidth - 1 - x) + y) * 2 + 1] = ch2; + newattrs[(subheight * (subwidth - 1 - x) + y) * 2 + 1] = attr2; + } + } + + free(cv->chars); + free(cv->attrs); + + /* Swap X and Y information */ + x = cv->frames[cv->frame].x; + y = cv->frames[cv->frame].y; + cv->frames[cv->frame].x = y * 2; + cv->frames[cv->frame].y = (cv->width - 1 - x) / 2; + + x = cv->frames[cv->frame].handlex; + y = cv->frames[cv->frame].handley; + cv->frames[cv->frame].handlex = y * 2; + cv->frames[cv->frame].handley = (cv->width - 1 - x) / 2; + + cv->frames[cv->frame].width = cv->height * 2; + cv->frames[cv->frame].height = cv->width / 2; + + cv->frames[cv->frame].chars = newchars; + cv->frames[cv->frame].attrs = newattrs; + + /* Reset the current frame shortcuts */ + _cucul_load_frame_info(cv); + + return 0; +} + /** \brief Rotate a canvas, 90 degrees clockwise. * * Apply a 270-degree transformation to a canvas, choosing characters @@ -393,6 +493,106 @@ int cucul_rotate_right(cucul_canvas_t *cv) return 0; } +/** \brief Rotate a canvas, 90 degrees counterclockwise (widechar version). + * + * Apply a 90-degree transformation to a canvas, choosing characters + * that look like the rotated version wherever possible. Characters cells + * are rotated two-by-two. Some characters will stay unchanged by the + * process, some others will be replaced by close equivalents. Fullwidth + * characters at odd horizontal coordinates will be lost. The operation is + * not guaranteed to be reversible at all. + * + * Note that the width of the canvas is divided by two and becomes the + * new height. Height is multiplied by two and becomes the new width. It + * is illegal to pass a canvas with an odd width to this function. + * + * If an error occurs, -1 is returned and \b errno is set accordingly: + * - \c EBUSY The canvas is in use by a display driver and cannot be rotated. + * - \c EINVAL The canvas' width is odd. + * - \c ENOMEM Not enough memory to allocate the new canvas size. If this + * happens, the previous canvas handle is still valid. + * + * \param cv The canvas to rotate right. + * \return 0 in case of success, -1 if an error occurred. + */ +int cucul_rotate_right_wide(cucul_canvas_t *cv) +{ + uint32_t *newchars, *newattrs; + unsigned int x, y, subwidth, subheight; + + if(cv->refcount) + { + seterrno(EBUSY); + return -1; + } + + if(cv->width & 1) + { + seterrno(EINVAL); + return -1; + } + + /* Save the current frame shortcuts */ + _cucul_save_frame_info(cv); + + newchars = malloc(cv->width * cv->height * sizeof(uint32_t)); + if(!newchars) + return -1; + + newattrs = malloc(cv->width * cv->height * sizeof(uint32_t)); + if(!newattrs) + { + free(newchars); + return -1; + } + + subwidth = cv->width / 2; + subheight = cv->height; + + for(y = 0; y < subheight; y++) + { + for(x = 0; x < subwidth; x++) + { + uint32_t ch1, ch2, attr1, attr2; + + ch1 = cv->chars[(subwidth * y + x) * 2]; + attr1 = cv->attrs[(subwidth * y + x) * 2]; + ch2 = cv->chars[(subwidth * y + x) * 2 + 1]; + attr2 = cv->attrs[(subwidth * y + x) * 2 + 1]; + + newchars[(subheight * x + subheight - 1 - y) * 2] = ch1; + newattrs[(subheight * x + subheight - 1 - y) * 2] = attr1; + newchars[(subheight * x + subheight - 1 - y) * 2 + 1] = ch2; + newattrs[(subheight * x + subheight - 1 - y) * 2 + 1] = attr2; + } + } + + free(cv->chars); + free(cv->attrs); + + /* Swap X and Y information */ + x = cv->frames[cv->frame].x; + y = cv->frames[cv->frame].y; + cv->frames[cv->frame].x = (cv->height - 1 - y) * 2; + cv->frames[cv->frame].y = x / 2; + + x = cv->frames[cv->frame].handlex; + y = cv->frames[cv->frame].handley; + cv->frames[cv->frame].handlex = (cv->height - 1 - y) * 2; + cv->frames[cv->frame].handley = x / 2; + + cv->frames[cv->frame].width = cv->height * 2; + cv->frames[cv->frame].height = cv->width / 2; + + cv->frames[cv->frame].chars = newchars; + cv->frames[cv->frame].attrs = newattrs; + + /* Reset the current frame shortcuts */ + _cucul_load_frame_info(cv); + + return 0; +} + /* FIXME: as the lookup tables grow bigger, use a log(n) lookup instead * of linear lookup. */ static uint32_t flipchar(uint32_t ch) diff --git a/test/text.c b/test/text.c index ac13deb..4e246f1 100644 --- a/test/text.c +++ b/test/text.c @@ -68,7 +68,7 @@ int main(int argc, char *argv[]) fwrite(buffer, len, 1, stdout); free(buffer); - cucul_rotate_left(cv); + cucul_rotate_left_wide(cv); buffer = cucul_export_memory(cv, "utf8", &len); fwrite(buffer, len, 1, stdout); free(buffer);