欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

MTK Recovery 模式横屏修改(适用于6.0 + 8.1)

程序员文章站 2022-07-02 22:06:47
修改前 修改后 6.0 Recovery 模式横屏修改方法 修改相关文件 bootable\recovery\minui\Android.mk bootable\recovery\minui\mt_graphic_rotate.cpp bootable\recovery\minui\mt_graph ......

MTK Recovery 模式横屏修改(适用于6.0 + 8.1)

修改前

MTK Recovery 模式横屏修改(适用于6.0 + 8.1)

修改后

6.0 recovery 模式横屏修改方法

修改相关文件
bootable\recovery\minui\android.mk
bootable\recovery\minui\mt_graphic_rotate.cpp
bootable\recovery\minui\mt_graphic_rotate.h
bootable\recovery\minui\graphics_fbdev.cpp
bootable\recovery\minui\minui.h

1、在 android.mk 中增加 mt_graphic_rotate.cpp 编译项

local_src_files := \
    events.cpp \
    graphics.cpp \
    graphics_adf.cpp \
    graphics_drm.cpp \
    graphics_fbdev.cpp \
    resources.cpp \

#cczheng add 
local_src_files += mt_graphic_rotate.cpp

2、对比修改 mt_graphic_rotate.cpp,没有则直接复制以下类

/*
* copyright (c) 2014 mediatek inc.
* modification based on code covered by the mentioned copyright
* and/or permission notice(s).
*/


#include <stdbool.h>
#include <stdlib.h>
#include <unistd.h>
#include <inttypes.h>

#include <fcntl.h>
#include <stdio.h>

#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>

#include <linux/fb.h>
#include <linux/kd.h>

#include "minui.h"
#include "graphics.h"

grsurface __gr_canvas;

grsurface* gr_canvas = null;
int rotate_index=-1;

static void print_surface_info(grsurface *s, const char *name)
{
    printf("[graphics] %s > height:%d, width:%d, pixelbytes:%d, rowbytes:%d, size:%d, data: 0x%08" prixptr "\n",
        name, s->height, s->width, s->pixel_bytes, s->row_bytes, s->height* s->row_bytes, (uintptr_t) s->data);
}

// read configuration from mtk_lcm_physical_rotation
#ifndef mtk_lcm_physical_rotation
#define mtk_lcm_physical_rotation "undefined"
#endif
static int rotate_config(grsurface *gr_draw)
{
    if (rotate_index<0)
    {
        if (gr_draw->pixel_bytes != 4) rotate_index=0; // support 4 bytes pixel only
        else if (0 == strncmp(mtk_lcm_physical_rotation, "90", 2)) rotate_index=1;
        else if (0 == strncmp(mtk_lcm_physical_rotation, "180", 3)) rotate_index=2;
        else if (0 == strncmp(mtk_lcm_physical_rotation, "270", 3)) rotate_index=3;
        else rotate_index=0;
        printf("[graphics] rotate_config %d %s\n", rotate_index, mtk_lcm_physical_rotation);
    }
    return rotate_index;
}

#define swap(x, y, type) {type z; z=x; x=y; y=z;}

// allocate and setup the canvas object
void rotate_canvas_init(grsurface *gr_draw)
{
    gr_canvas = &__gr_canvas;
    memcpy(gr_canvas, gr_draw, sizeof(grsurface));

    // swap canvas' height and width, if the rotate angle is 90" or 270"
    if (rotate_config(gr_draw)%2) {
        swap(gr_canvas->width, gr_canvas->height, int);
        gr_canvas->row_bytes = gr_canvas->width * gr_canvas->pixel_bytes;
    }

    gr_canvas->data = (unsigned char*) malloc(gr_canvas->height * gr_canvas->row_bytes);
    if (gr_canvas->data == null) {
        printf("[graphics] rotate_canvas_init() malloc gr_canvas->data failed\n");
        gr_canvas = null;
        return;
    }

    memset(gr_canvas->data,  0, gr_canvas->height * gr_canvas->row_bytes);

    print_surface_info(gr_draw, "gr_draw");
    print_surface_info(gr_canvas, "gr_canvas");
}

// cleanup the canvas
void rotate_canvas_exit(void)
{
    if (gr_canvas) {
        if (gr_canvas->data)
            free(gr_canvas->data);
        free(gr_canvas);
    }
    gr_canvas=null;
}

// return the canvas object
grsurface *rotate_canvas_get(grsurface *gr_draw)
{
    // initialize the canvas, if it was not exist.
    if (gr_canvas==null)
        rotate_canvas_init(gr_draw);
    return gr_canvas;
}

// surface rotate routines
static void rotate_surface_0(grsurface *dst, grsurface *src)
{
    memcpy(dst->data, src->data, src->height*src->row_bytes);
}

static void rotate_surface_270(grsurface *dst, grsurface *src)
{
    int v, w, h;
    unsigned int *src_pixel;
    unsigned int *dst_pixel;

    for (h=0, v=src->width-1; h<dst->height; h++, v--) {
        for (w=0; w<dst->width; w++) {
            dst_pixel = (unsigned int *)(dst->data + dst->row_bytes*h);
            src_pixel = (unsigned int *)(src->data + src->row_bytes*w);
            *(dst_pixel+w)=*(src_pixel+v);
        }
    }
}

static void rotate_surface_180(grsurface *dst, grsurface *src)
{
    int v, w, k, h;
    unsigned int *src_pixel;
    unsigned int *dst_pixel;

    for (h=0, k=src->height-1; h<dst->height && k>=0 ; h++, k--) {
        dst_pixel = (unsigned int *)(dst->data + dst->row_bytes*h);
        src_pixel = (unsigned int *)(src->data + src->row_bytes*k);
        for (w=0, v=src->width-1; w<dst->width && v>=0; w++, v--) {
            *(dst_pixel+w)=*(src_pixel+v);
        }
    }
}

static void rotate_surface_90(grsurface *dst, grsurface *src)
{
    int w, k, h;
    unsigned int *src_pixel;
    unsigned int *dst_pixel;

    for (h=0; h<dst->height; h++) {
        for (w=0, k=src->height-1; w<dst->width; w++, k--) {
            dst_pixel = (unsigned int *)(dst->data + dst->row_bytes*h);
            src_pixel = (unsigned int *)(src->data + src->row_bytes*k);
            *(dst_pixel+w)=*(src_pixel+h);
        }
    }
}

typedef void (*rotate_surface_t) (grsurface *, grsurface *);

rotate_surface_t rotate_func[4]=
{
    rotate_surface_0,
    rotate_surface_90,
    rotate_surface_180,
    rotate_surface_270
};

// rotate and copy src* surface to dst surface
void rotate_surface(grsurface *dst, grsurface *src)
{
    rotate_surface_t rotate;
    rotate=rotate_func[rotate_config(dst)];
    rotate(dst, src);
}

3、对比修改 mt_graphic_rotate.h,没有则直接复制以下类

/*
* copyright (c) 2014 mediatek inc.
* modification based on code covered by the mentioned copyright
* and/or permission notice(s).
*/

#ifndef mt_graphics_rotate_h_
#define mt_graphics_rotate_h_

#include "minui.h"

void rotate_canvas_exit(void);
void rotate_canvas_init(grsurface *gr_draw);
void rotate_surface(grsurface *dst, grsurface *src);
grsurface *rotate_canvas_get(grsurface *gr_draw);

#endif

4、对比修改 graphics_fbdev.cpp,没有则直接复制以下类

/*
 * copyright (c) 2014 the android open source project
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *      http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */

#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <fcntl.h>
#include <stdio.h>

#include <sys/cdefs.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>

#include <linux/fb.h>
#include <linux/kd.h>

#include "minui.h"
#include "graphics.h"
#include "mt_graphic_rotate.h"

static grsurface* fbdev_init(minui_backend*);
static grsurface* fbdev_flip(minui_backend*);
static void fbdev_blank(minui_backend*, bool);
static void fbdev_exit(minui_backend*);

static grsurface gr_framebuffer[2];
static bool double_buffered;
static grsurface* gr_draw = null;
static int displayed_buffer;

static fb_var_screeninfo vi;
static int fb_fd = -1;

static minui_backend my_backend = {
    .init = fbdev_init,
    .flip = fbdev_flip,
    .blank = fbdev_blank,
    .exit = fbdev_exit,
};

minui_backend* open_fbdev() {
    return &my_backend;
}

static void fbdev_blank(minui_backend* backend __unused, bool blank)
{
    int ret;

    ret = ioctl(fb_fd, fbioblank, blank ? fb_blank_powerdown : fb_blank_unblank);
    if (ret < 0)
        perror("ioctl(): blank");
}

static void set_displayed_framebuffer(unsigned n)
{
    if (n > 1 || !double_buffered) return;

    vi.yres_virtual = gr_framebuffer[0].height * 2;
    vi.yoffset = n * gr_framebuffer[0].height;
    vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
    if (ioctl(fb_fd, fbioput_vscreeninfo, &vi) < 0) {
        perror("active fb swap failed");
    }
    displayed_buffer = n;
}

static grsurface* fbdev_init(minui_backend* backend) {
    int fd = open("/dev/graphics/fb0", o_rdwr);
    if (fd == -1) {
        perror("cannot open fb0");
        return null;
    }

    fb_fix_screeninfo fi;
    if (ioctl(fd, fbioget_fscreeninfo, &fi) < 0) {
        perror("failed to get fb0 info");
        close(fd);
        return null;
    }

    if (ioctl(fd, fbioget_vscreeninfo, &vi) < 0) {
        perror("failed to get fb0 info");
        close(fd);
        return null;
    }

    // we print this out for informational purposes only, but
    // throughout we assume that the framebuffer device uses an rgbx
    // pixel format.  this is the case for every development device i
    // have access to.  for some of those devices (eg, hammerhead aka
    // nexus 5), fbioget_vscreeninfo *reports* that it wants a
    // different format (xbgr) but actually produces the correct
    // results on the display when you write rgbx.
    //
    // if you have a device that actually *needs* another pixel format
    // (ie, bgrx, or 565), patches welcome...

    printf("fb0 reports (possibly inaccurate):\n"
           "  vi.bits_per_pixel = %d\n"
           "  vi.red.offset   = %3d   .length = %3d\n"
           "  vi.green.offset = %3d   .length = %3d\n"
           "  vi.blue.offset  = %3d   .length = %3d\n",
           vi.bits_per_pixel,
           vi.red.offset, vi.red.length,
           vi.green.offset, vi.green.length,
           vi.blue.offset, vi.blue.length);

    void* bits = mmap(0, fi.smem_len, prot_read | prot_write, map_shared, fd, 0);
    if (bits == map_failed) {
        perror("failed to mmap framebuffer");
        close(fd);
        return null;
    }

    memset(bits, 0, fi.smem_len);

    gr_framebuffer[0].width = vi.xres;
    gr_framebuffer[0].height = vi.yres;
    gr_framebuffer[0].row_bytes = fi.line_length;
    gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
    gr_framebuffer[0].data = reinterpret_cast<uint8_t*>(bits);
    memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);

    /* check if we can use double buffering */
    printf("[graphics] vi.yres * fi.line_length = %d * %d * 2 = %d, fi.smem_len=%d\n",
        vi.yres, fi.line_length, vi.yres * fi.line_length * 2, fi.smem_len);

    if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
        double_buffered = true;
        printf("[graphics] double buffered\n");

        memcpy(gr_framebuffer+1, gr_framebuffer, sizeof(grsurface));
        gr_framebuffer[1].data = gr_framebuffer[0].data +
            gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;

        gr_draw = gr_framebuffer+1;

    } else {
        double_buffered = false;
        printf("[graphics] without double buffer\n");
        // without double-buffering, we allocate ram for a buffer to
        // draw in, and then "flipping" the buffer consists of a
        // memcpy from the buffer we allocated to the framebuffer.

        gr_draw = (grsurface*) malloc(sizeof(grsurface));
        memcpy(gr_draw, gr_framebuffer, sizeof(grsurface));
        gr_draw->data = (unsigned char*) malloc(gr_draw->height * gr_draw->row_bytes);
        if (!gr_draw->data) {
            perror("failed to allocate in-memory surface");
            return null;
        }
    }

    memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
    fb_fd = fd;
    set_displayed_framebuffer(0);

    printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);

#if 0 // to avoid display blink due to display driver not disable backlight after kernel standardization, so that temp state between display suspend/resume is shown
    fbdev_blank(backend, true);
    fbdev_blank(backend, false);
#endif

    return rotate_canvas_get(gr_draw);
}

static grsurface* fbdev_flip(minui_backend* backend __unused) {
    rotate_surface(gr_draw, rotate_canvas_get(gr_draw));
    if (double_buffered) {
#if defined(recovery_bgra)
        // in case of bgra, do some byte swapping
        unsigned int idx;
        unsigned char tmp;
        unsigned char* ucfb_vaddr = (unsigned char*)gr_draw->data;
        for (idx = 0 ; idx < (gr_draw->height * gr_draw->row_bytes);
                idx += 4) {
            tmp = ucfb_vaddr[idx];
            ucfb_vaddr[idx    ] = ucfb_vaddr[idx + 2];
            ucfb_vaddr[idx + 2] = tmp;
        }
#endif
        // change gr_draw to point to the buffer currently displayed,
        // then flip the driver so we're displaying the other buffer
        // instead.
        gr_draw = gr_framebuffer + displayed_buffer;
        set_displayed_framebuffer(1-displayed_buffer);
    } else {
        // copy from the in-memory surface to the framebuffer.
        memcpy(gr_framebuffer[0].data, gr_draw->data,
               gr_draw->height * gr_draw->row_bytes);
    }
    return rotate_canvas_get(gr_draw);
}

static void fbdev_exit(minui_backend* backend __unused) {
    close(fb_fd);
    fb_fd = -1;
    rotate_canvas_exit();
    if (!double_buffered && gr_draw) {
        free(gr_draw->data);
        free(gr_draw);
    }
    gr_draw = null;
}

5、对比修改 minui.h,没有则直接复制以下类

/*
 * copyright (c) 2007 the android open source project
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *      http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */

#ifndef _minui_h_
#define _minui_h_

#include <sys/types.h>

#include <functional>

//
// graphics.
//

struct grsurface {
    int width;
    int height;
    int row_bytes;
    int pixel_bytes;
    unsigned char* data;
};

int gr_init();
void gr_exit();

int gr_fb_width();
int gr_fb_height();

void gr_flip();
void gr_fb_blank(bool blank);

void gr_clear();  // clear entire surface to current color
void gr_color(unsigned char r, unsigned char g, unsigned char b, unsigned char a);
void gr_fill(int x1, int y1, int x2, int y2);
void gr_text(int x, int y, const char *s, bool bold);
void gr_texticon(int x, int y, grsurface* icon);
int gr_measure(const char *s);
void gr_font_size(int *x, int *y);

void gr_blit(grsurface* source, int sx, int sy, int w, int h, int dx, int dy);
unsigned int gr_get_width(grsurface* surface);
unsigned int gr_get_height(grsurface* surface);

//
// input events.
//

struct input_event;

// todo: move these over to std::function.
typedef int (*ev_callback)(int fd, uint32_t epevents, void* data);
typedef int (*ev_set_key_callback)(int code, int value, void* data);

int ev_init(ev_callback input_cb, void* data);
void ev_exit();
int ev_add_fd(int fd, ev_callback cb, void* data);
void ev_iterate_available_keys(std::function<void(int)> f);
int ev_sync_key_state(ev_set_key_callback set_key_cb, void* data);

// 'timeout' has the same semantics as poll(2).
//    0 : don't block
//  < 0 : block forever
//  > 0 : block for 'timeout' milliseconds
int ev_wait(int timeout);

int ev_get_input(int fd, uint32_t epevents, input_event* ev);
void ev_dispatch();
int ev_get_epollfd();

//
// resources
//

// res_create_*_surface() functions return 0 if no error, else
// negative.
//
// a "display" surface is one that is intended to be drawn to the
// screen with gr_blit().  an "alpha" surface is a grayscale image
// interpreted as an alpha mask used to render text in the current
// color (with gr_text() or gr_texticon()).
//
// all these functions load png images from "/res/images/${name}.png".

// load a single display surface from a png image.
int res_create_display_surface(const char* name, grsurface** psurface);

// load an array of display surfaces from a single png image.  the png
// should have a 'frames' text chunk whose value is the number of
// frames this image represents.  the pixel data itself is interlaced
// by row.
int res_create_multi_display_surface(const char* name,
                                     int* frames, grsurface*** psurface);

// load a single alpha surface from a grayscale png image.
int res_create_alpha_surface(const char* name, grsurface** psurface);

// load part of a grayscale png image that is the first match for the
// given locale.  the image is expected to be a composite of multiple
// translations of the same text, with special added rows that encode
// the subimages' size and intended locale in the pixel data.  see
// development/tools/recovery_l10n for an app that will generate these
// specialized images from android resources.
int res_create_localized_alpha_surface(const char* name, const char* locale,
                                       grsurface** psurface);

// free a surface allocated by any of the res_create_*_surface()
// functions.
void res_free_surface(grsurface* surface);

#endif

8.1 recovery 模式横屏修改方法

8.1 的修改步骤和 6.0 大体差不多,前三步都一样,请参考上面的,由于 8.1 中不需要 minui.h 文件,所以 mt_graphic_rotate.hmt_graphic_rotate.cpp 中 需要注释

//#include "minui.h"

4、对比修改 graphics_fbdev.cpp

/*
 * copyright (c) 2014 the android open source project
 *
 * licensed under the apache license, version 2.0 (the "license");
 * you may not use this file except in compliance with the license.
 * you may obtain a copy of the license at
 *
 *      http://www.apache.org/licenses/license-2.0
 *
 * unless required by applicable law or agreed to in writing, software
 * distributed under the license is distributed on an "as is" basis,
 * without warranties or conditions of any kind, either express or implied.
 * see the license for the specific language governing permissions and
 * limitations under the license.
 */

#include "graphics_fbdev.h"

#include <fcntl.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>

#include "minui/minui.h"
//cczheng add for rotatecanvas
#include "mt_graphic_rotate.h"

minuibackendfbdev::minuibackendfbdev() : gr_draw(nullptr), fb_fd(-1) {}

void minuibackendfbdev::blank(bool blank) {
  int ret = ioctl(fb_fd, fbioblank, blank ? fb_blank_powerdown : fb_blank_unblank);
  if (ret < 0) perror("ioctl(): blank");
}

void minuibackendfbdev::setdisplayedframebuffer(unsigned n) {
  if (n > 1 || !double_buffered) return;

  vi.yres_virtual = gr_framebuffer[0].height * 2;
  vi.yoffset = n * gr_framebuffer[0].height;
  vi.bits_per_pixel = gr_framebuffer[0].pixel_bytes * 8;
  if (ioctl(fb_fd, fbioput_vscreeninfo, &vi) < 0) {
    perror("active fb swap failed");
  }
  displayed_buffer = n;
}

grsurface* minuibackendfbdev::init() {
  int fd = open("/dev/graphics/fb0", o_rdwr);
  if (fd == -1) {
    perror("cannot open fb0");
    return nullptr;
  }

  fb_fix_screeninfo fi;
  if (ioctl(fd, fbioget_fscreeninfo, &fi) < 0) {
    perror("failed to get fb0 info");
    close(fd);
    return nullptr;
  }

  if (ioctl(fd, fbioget_vscreeninfo, &vi) < 0) {
    perror("failed to get fb0 info");
    close(fd);
    return nullptr;
  }

  // we print this out for informational purposes only, but
  // throughout we assume that the framebuffer device uses an rgbx
  // pixel format.  this is the case for every development device i
  // have access to.  for some of those devices (eg, hammerhead aka
  // nexus 5), fbioget_vscreeninfo *reports* that it wants a
  // different format (xbgr) but actually produces the correct
  // results on the display when you write rgbx.
  //
  // if you have a device that actually *needs* another pixel format
  // (ie, bgrx, or 565), patches welcome...

  printf(
      "fb0 reports (possibly inaccurate):\n"
      "  vi.bits_per_pixel = %d\n"
      "  vi.red.offset   = %3d   .length = %3d\n"
      "  vi.green.offset = %3d   .length = %3d\n"
      "  vi.blue.offset  = %3d   .length = %3d\n",
      vi.bits_per_pixel, vi.red.offset, vi.red.length, vi.green.offset, vi.green.length,
      vi.blue.offset, vi.blue.length);

  void* bits = mmap(0, fi.smem_len, prot_read | prot_write, map_shared, fd, 0);
  if (bits == map_failed) {
    perror("failed to mmap framebuffer");
    close(fd);
    return nullptr;
  }

  memset(bits, 0, fi.smem_len);

  gr_framebuffer[0].width = vi.xres;
  gr_framebuffer[0].height = vi.yres;
  gr_framebuffer[0].row_bytes = fi.line_length;
  gr_framebuffer[0].pixel_bytes = vi.bits_per_pixel / 8;
  gr_framebuffer[0].data = static_cast<uint8_t*>(bits);
  memset(gr_framebuffer[0].data, 0, gr_framebuffer[0].height * gr_framebuffer[0].row_bytes);

  /* check if we can use double buffering */
  if (vi.yres * fi.line_length * 2 <= fi.smem_len) {
    double_buffered = true;

    memcpy(gr_framebuffer + 1, gr_framebuffer, sizeof(grsurface));
    gr_framebuffer[1].data =
        gr_framebuffer[0].data + gr_framebuffer[0].height * gr_framebuffer[0].row_bytes;

    gr_draw = gr_framebuffer + 1;

  } else {
    double_buffered = false;

    // without double-buffering, we allocate ram for a buffer to
    // draw in, and then "flipping" the buffer consists of a
    // memcpy from the buffer we allocated to the framebuffer.

    gr_draw = static_cast<grsurface*>(malloc(sizeof(grsurface)));
    memcpy(gr_draw, gr_framebuffer, sizeof(grsurface));
    gr_draw->data = static_cast<unsigned char*>(malloc(gr_draw->height * gr_draw->row_bytes));
    if (!gr_draw->data) {
      perror("failed to allocate in-memory surface");
      return nullptr;
    }
  }

  memset(gr_draw->data, 0, gr_draw->height * gr_draw->row_bytes);
  fb_fd = fd;
  setdisplayedframebuffer(0);

  printf("framebuffer: %d (%d x %d)\n", fb_fd, gr_draw->width, gr_draw->height);
#if 0
  blank(true);
  blank(false);
#endif

  //return gr_draw;
  //cczheng add for rotatecanvas
  return rotate_canvas_get(gr_draw);
}

grsurface* minuibackendfbdev::flip() {
  //cczheng add for rotatecanvas
  rotate_surface(gr_draw, rotate_canvas_get(gr_draw));
  if (double_buffered) {
    // change gr_draw to point to the buffer currently displayed,
    // then flip the driver so we're displaying the other buffer
    // instead.
    gr_draw = gr_framebuffer + displayed_buffer;
    setdisplayedframebuffer(1 - displayed_buffer);
  } else {
    // copy from the in-memory surface to the framebuffer.
    memcpy(gr_framebuffer[0].data, gr_draw->data, gr_draw->height * gr_draw->row_bytes);
  }
  //return gr_draw;
  //cczheng add for rotatecanvas
  return rotate_canvas_get(gr_draw);
}

minuibackendfbdev::~minuibackendfbdev() {
  close(fb_fd);
  fb_fd = -1;
  //cczheng add for rotatecanvas
  rotate_canvas_exit();
  if (!double_buffered && gr_draw) {
    free(gr_draw->data);
    free(gr_draw);
  }
  gr_draw = nullptr;
}

修改旋转角度,在 mt_graphic_rotate.cpp 中的 rotate_config(grsurface gr_draw)
旋转90度就将 return rotate_index 直接改为 return 1,
依次类推转 180度, 改为 return 2, 270 度改为 return 3.
注意旋转方向为顺时针旋转

我修改的情况对应 return 3.