Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d90da130 authored by Doug Zongker's avatar Doug Zongker Committed by Gerrit Code Review
Browse files

Merge "refactor image resource loading code in minui"

parents 026ebe02 a418aa7d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -269,7 +269,7 @@ static void gr_init_font(void)
{
    gr_font = calloc(sizeof(*gr_font), 1);

    int res = res_create_surface("font", &(gr_font->texture));
    int res = res_create_alpha_surface("font", &(gr_font->texture));
    if (res == 0) {
        // The font image should be a 96x2 array of character images.  The
        // columns are the printable ASCII characters 0x20 - 0x7f.  The
+34 −4
Original line number Diff line number Diff line
@@ -80,10 +80,40 @@ void ev_dispatch(void);

// Resources

// Returns 0 if no error, else negative.
int res_create_surface(const char* name, gr_surface* pSurface);
int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface);
int res_create_localized_surface(const char* name, gr_surface* pSurface);
// 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, gr_surface* 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, gr_surface** pSurface);

// Load a single alpha surface from a grayscale PNG image.
int res_create_alpha_surface(const char* name, gr_surface* 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,
                                       gr_surface* pSurface);

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

#ifdef __cplusplus
+207 −233
Original line number Diff line number Diff line
@@ -44,15 +44,11 @@ static gr_surface malloc_surface(size_t data_size) {
    return surface;
}

int res_create_surface(const char* name, gr_surface* pSurface) {
static int open_png(const char* name, png_structp* png_ptr, png_infop* info_ptr,
                    png_uint_32* width, png_uint_32* height, png_byte* channels) {
    char resPath[256];
    gr_surface surface = NULL;
    int result = 0;
    unsigned char header[8];
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;

    *pSurface = NULL;
    int result = 0;

    snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
    resPath[sizeof(resPath)-1] = '\0';
@@ -73,170 +69,182 @@ int res_create_surface(const char* name, gr_surface* pSurface) {
        goto exit;
    }

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) {
    *png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!*png_ptr) {
        result = -4;
        goto exit;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
    *info_ptr = png_create_info_struct(*png_ptr);
    if (!*info_ptr) {
        result = -5;
        goto exit;
    }

    if (setjmp(png_jmpbuf(png_ptr))) {
    if (setjmp(png_jmpbuf(*png_ptr))) {
        result = -6;
        goto exit;
    }

    png_init_io(png_ptr, fp);
    png_set_sig_bytes(png_ptr, sizeof(header));
    png_read_info(png_ptr, info_ptr);
    png_init_io(*png_ptr, fp);
    png_set_sig_bytes(*png_ptr, sizeof(header));
    png_read_info(*png_ptr, *info_ptr);

    int color_type, bit_depth;
    png_uint_32 width, height;
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
    png_get_IHDR(*png_ptr, *info_ptr, width, height, &bit_depth,
            &color_type, NULL, NULL, NULL);

    int channels = png_get_channels(png_ptr, info_ptr);

    if (!(bit_depth <= 8 &&
          ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
           (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE ||
                              color_type == PNG_COLOR_TYPE_GRAY))))) {
        return -7;
    *channels = png_get_channels(*png_ptr, *info_ptr);

    if (bit_depth == 8 && *channels == 3 && color_type == PNG_COLOR_TYPE_RGB) {
        // 8-bit RGB images: great, nothing to do.
    } else if (bit_depth <= 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_GRAY) {
        // 1-, 2-, 4-, or 8-bit gray images: expand to 8-bit gray.
        png_set_expand_gray_1_2_4_to_8(*png_ptr);
    } else if (bit_depth == 8 && *channels == 1 && color_type == PNG_COLOR_TYPE_PALETTE) {
        // paletted images: expand to 8-bit RGB.  Note that we DON'T
        // currently expand the tRNS chunk (if any) to an alpha
        // channel, because minui doesn't support alpha channels in
        // general.
        png_set_palette_to_rgb(*png_ptr);
        *channels = 3;
    } else {
        fprintf(stderr, "minui doesn't support PNG depth %d channels %d color_type %d\n",
                bit_depth, (int) channels, color_type);
        result = -7;
        goto exit;
    }

    size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
    return result;

    surface = malloc_surface(stride * height);
    if (surface == NULL) {
        result = -8;
        goto exit;
  exit:
    if (result < 0) {
        png_destroy_read_struct(png_ptr, info_ptr, NULL);
    }
    if (fp != NULL) {
        fclose(fp);
    }
    unsigned char* pData = surface->data;
    surface->width = width;
    surface->height = height;
    surface->row_bytes = stride;
    surface->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4);

    int alpha = (channels == 4);
    png_set_expand(png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY) {
        alpha = 1;
    return result;
}

    unsigned int y;
    if (channels == 3 || (channels == 1 && !alpha)) {
        for (y = 0; y < height; ++y) {
            unsigned char* pRow = pData + y * stride;
            png_read_row(png_ptr, pRow, NULL);
// "display" surfaces are transformed into the framebuffer's required
// pixel format (currently only RGBX is supported) at load time, so
// gr_blit() can be nothing more than a memcpy() for each row.  The
// next two functions are the only ones that know anything about the
// framebuffer pixel format; they need to be modified if the
// framebuffer format changes (but nothing else should).

            int x;
            for(x = width - 1; x >= 0; x--) {
                int sx = x * 3;
                int dx = x * 4;
                unsigned char r = pRow[sx];
                unsigned char g = pRow[sx + 1];
                unsigned char b = pRow[sx + 2];
                unsigned char a = 0xff;
                pRow[dx    ] = r; // r
                pRow[dx + 1] = g; // g
                pRow[dx + 2] = b; // b
                pRow[dx + 3] = a;
            }
        }
    } else {
        for (y = 0; y < height; ++y) {
            unsigned char* pRow = pData + y * stride;
            png_read_row(png_ptr, pRow, NULL);
        }
    }
// Allocate and return a gr_surface sufficient for storing an image of
// the indicated size in the framebuffer pixel format.
static gr_surface init_display_surface(png_uint_32 width, png_uint_32 height) {
    gr_surface surface;

    *pSurface = (gr_surface) surface;
    surface = malloc_surface(width * height * 4);
    if (surface == NULL) return NULL;

exit:
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    surface->width = width;
    surface->height = height;
    surface->row_bytes = width * 4;
    surface->pixel_bytes = 4;

    if (fp != NULL) {
        fclose(fp);
    return surface;
}
    if (result < 0) {
        if (surface) {
            free(surface);

// Copy 'input_row' to 'output_row', transforming it to the
// framebuffer pixel format.  The input format depends on the value of
// 'channels':
//
//   1 - input is 8-bit grayscale
//   3 - input is 24-bit RGB
//   4 - input is 32-bit RGBA/RGBX
//
// 'width' is the number of pixels in the row.
static void transform_rgb_to_draw(unsigned char* input_row,
                                  unsigned char* output_row,
                                  int channels, int width) {
    int x;
    unsigned char* ip = input_row;
    unsigned char* op = output_row;

    switch (channels) {
        case 1:
            // expand gray level to RGBX
            for (x = 0; x < width; ++x) {
                *op++ = *ip;
                *op++ = *ip;
                *op++ = *ip;
                *op++ = 0xff;
                ip++;
            }
            break;

        case 3:
            // expand RGBA to RGBX
            for (x = 0; x < width; ++x) {
                *op++ = *ip++;
                *op++ = *ip++;
                *op++ = *ip++;
                *op++ = 0xff;
            }
            break;

        case 4:
            // copy RGBA to RGBX
            memcpy(output_row, input_row, width*4);
            break;
    }
    return result;
}

int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurface) {
    char resPath[256];
int res_create_display_surface(const char* name, gr_surface* pSurface) {
    gr_surface surface = NULL;
    int result = 0;
    unsigned char header[8];
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    int i;
    gr_surface* surface = NULL;
    png_uint_32 width, height;
    png_byte channels;

    *pSurface = NULL;
    *frames = -1;

    snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
    resPath[sizeof(resPath)-1] = '\0';
    FILE* fp = fopen(resPath, "rb");
    if (fp == NULL) {
        result = -1;
        goto exit;
    }

    size_t bytesRead = fread(header, 1, sizeof(header), fp);
    if (bytesRead != sizeof(header)) {
        result = -2;
        goto exit;
    }
    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
    if (result < 0) return result;

    if (png_sig_cmp(header, 0, sizeof(header))) {
        result = -3;
    surface = init_display_surface(width, height);
    if (surface == NULL) {
        result = -8;
        goto exit;
    }

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) {
        result = -4;
        goto exit;
    unsigned char* p_row = malloc(width * 4);
    unsigned int y;
    for (y = 0; y < height; ++y) {
        png_read_row(png_ptr, p_row, NULL);
        transform_rgb_to_draw(p_row, surface->data + y * surface->row_bytes, channels, width);
    }
    free(p_row);

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        result = -5;
        goto exit;
    }
    *pSurface = surface;

    if (setjmp(png_jmpbuf(png_ptr))) {
        result = -6;
        goto exit;
  exit:
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    if (result < 0 && surface != NULL) free(surface);
    return result;
}

    png_init_io(png_ptr, fp);
    png_set_sig_bytes(png_ptr, sizeof(header));
    png_read_info(png_ptr, info_ptr);

    int color_type, bit_depth;
int res_create_multi_display_surface(const char* name, int* frames, gr_surface** pSurface) {
    gr_surface* surface = NULL;
    int result = 0;
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    png_uint_32 width, height;
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
            &color_type, NULL, NULL, NULL);
    png_byte channels;
    int i;

    int channels = png_get_channels(png_ptr, info_ptr);
    *pSurface = NULL;
    *frames = -1;

    if (!(bit_depth <= 8 &&
          ((channels == 3 && color_type == PNG_COLOR_TYPE_RGB) ||
           (channels == 1 && (color_type == PNG_COLOR_TYPE_PALETTE ||
                              color_type == PNG_COLOR_TYPE_GRAY))))) {
        return -7;
        goto exit;
    }
    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
    if (result < 0) return result;

    *frames = 1;
    png_textp text;
@@ -257,67 +265,35 @@ int res_create_multi_surface(const char* name, int* frames, gr_surface** pSurfac
        goto exit;
    }

    size_t stride = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4) * width;
    size_t pixelSize = stride * height / *frames;

    surface = malloc(*frames * sizeof(gr_surface));
    if (surface == NULL) {
        result = -8;
        goto exit;
    }
    for (i = 0; i < *frames; ++i) {
        surface[i] = malloc_surface(pixelSize);
        surface[i]->width = width;
        surface[i]->height = height / *frames;
        surface[i]->row_bytes = stride;
        surface[i]->pixel_bytes = (color_type == PNG_COLOR_TYPE_GRAY ? 1 : 4);
        surface[i] = init_display_surface(width, height / *frames);
        if (surface[i] == NULL) {
            result = -8;
            goto exit;
        }

    int alpha = (channels == 4);
    png_set_expand(png_ptr);
    if (color_type == PNG_COLOR_TYPE_GRAY) {
        alpha = 1;
    }

    png_uint_32 y;
    if (channels == 3 || (channels == 1 && !alpha)) {
        for (y = 0; y < height; ++y) {
            int fy = y / *frames;
            int fr = y % *frames;
            unsigned char* pRow = surface[fr]->data + fy * stride;
            png_read_row(png_ptr, pRow, NULL);

            int x;
            for(x = width - 1; x >= 0; x--) {
                int sx = x * 3;
                int dx = x * 4;
                unsigned char r = pRow[sx];
                unsigned char g = pRow[sx + 1];
                unsigned char b = pRow[sx + 2];
                unsigned char a = 0xff;
                pRow[dx    ] = r; // r
                pRow[dx + 1] = g; // g
                pRow[dx + 2] = b; // b
                pRow[dx + 3] = a;
            }
        }
    } else {
    unsigned char* p_row = malloc(width * 4);
    unsigned int y;
    for (y = 0; y < height; ++y) {
            int fy = y / *frames;
            int fr = y % *frames;
            unsigned char* pRow = surface[fr]->data + fy * stride;
            png_read_row(png_ptr, pRow, NULL);
        }
        png_read_row(png_ptr, p_row, NULL);
        int frame = y % *frames;
        unsigned char* out_row = surface[frame]->data +
            (y / *frames) * surface[frame]->row_bytes;
        transform_rgb_to_draw(p_row, out_row, channels, width);
    }
    free(p_row);

    *pSurface = (gr_surface*) surface;

exit:
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

    if (fp != NULL) {
        fclose(fp);
    }
    if (result < 0) {
        if (surface) {
            for (i = 0; i < *frames; ++i) {
@@ -329,7 +305,50 @@ exit:
    return result;
}

static int matches_locale(const char* loc) {
int res_create_alpha_surface(const char* name, gr_surface* pSurface) {
    gr_surface surface = NULL;
    int result = 0;
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    png_uint_32 width, height;
    png_byte channels;

    *pSurface = NULL;

    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
    if (result < 0) return result;

    if (channels != 1) {
        result = -7;
        goto exit;
    }

    surface = malloc_surface(width * height);
    if (surface == NULL) {
        result = -8;
        goto exit;
    }
    surface->width = width;
    surface->height = height;
    surface->row_bytes = width;
    surface->pixel_bytes = 1;

    unsigned char* p_row;
    unsigned int y;
    for (y = 0; y < height; ++y) {
        p_row = surface->data + y * surface->row_bytes;
        png_read_row(png_ptr, p_row, NULL);
    }

    *pSurface = surface;

  exit:
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
    if (result < 0 && surface != NULL) free(surface);
    return result;
}

static int matches_locale(const char* loc, const char* locale) {
    if (locale == NULL) return 0;

    if (strcmp(loc, locale) == 0) return 1;
@@ -346,70 +365,35 @@ static int matches_locale(const char* loc) {
    return (strncmp(locale, loc, i) == 0 && locale[i] == '_');
}

int res_create_localized_surface(const char* name, gr_surface* pSurface) {
    char resPath[256];
int res_create_localized_alpha_surface(const char* name,
                                       const char* locale,
                                       gr_surface* pSurface) {
    gr_surface surface = NULL;
    int result = 0;
    unsigned char header[8];
    png_structp png_ptr = NULL;
    png_infop info_ptr = NULL;
    png_uint_32 width, height;
    png_byte channels;

    *pSurface = NULL;

    snprintf(resPath, sizeof(resPath)-1, "/res/images/%s.png", name);
    resPath[sizeof(resPath)-1] = '\0';
    FILE* fp = fopen(resPath, "rb");
    if (fp == NULL) {
        result = -1;
        goto exit;
    }

    size_t bytesRead = fread(header, 1, sizeof(header), fp);
    if (bytesRead != sizeof(header)) {
        result = -2;
        goto exit;
    }

    if (png_sig_cmp(header, 0, sizeof(header))) {
        result = -3;
        goto exit;
    }

    png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
    if (!png_ptr) {
        result = -4;
        goto exit;
    }

    info_ptr = png_create_info_struct(png_ptr);
    if (!info_ptr) {
        result = -5;
        goto exit;
    }

    if (setjmp(png_jmpbuf(png_ptr))) {
        result = -6;
    if (locale == NULL) {
        surface = malloc_surface(0);
        surface->width = 0;
        surface->height = 0;
        surface->row_bytes = 0;
        surface->pixel_bytes = 1;
        goto exit;
    }

    png_init_io(png_ptr, fp);
    png_set_sig_bytes(png_ptr, sizeof(header));
    png_read_info(png_ptr, info_ptr);

    int color_type, bit_depth;
    png_uint_32 width, height;
    png_get_IHDR(png_ptr, info_ptr, &width, &height, &bit_depth,
            &color_type, NULL, NULL, NULL);
    int channels = png_get_channels(png_ptr, info_ptr);
    result = open_png(name, &png_ptr, &info_ptr, &width, &height, &channels);
    if (result < 0) return result;

    if (!(bit_depth <= 8 &&
          (channels == 1 && color_type == PNG_COLOR_TYPE_GRAY))) {
        return -7;
    if (channels != 1) {
        result = -7;
        goto exit;
    }

    png_set_expand(png_ptr);

    unsigned char* row = malloc(width);
    png_uint_32 y;
    for (y = 0; y < height; ++y) {
@@ -419,7 +403,7 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) {
        int len = row[4];
        char* loc = (char*)row+5;

        if (y+1+h >= height || matches_locale(loc)) {
        if (y+1+h >= height || matches_locale(loc, locale)) {
            printf("  %20s: %s (%d x %d @ %d)\n", name, loc, w, h, y);

            surface = malloc_surface(w*h);
@@ -427,8 +411,6 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) {
                result = -8;
                goto exit;
            }
            unsigned char* pData = surface->data;

            surface->width = w;
            surface->height = h;
            surface->row_bytes = w;
@@ -437,7 +419,7 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) {
            int i;
            for (i = 0; i < h; ++i, ++y) {
                png_read_row(png_ptr, row, NULL);
                memcpy(pData + i*w, row, w);
                memcpy(surface->data + i*w, row, w);
            }

            *pSurface = (gr_surface) surface;
@@ -452,15 +434,7 @@ int res_create_localized_surface(const char* name, gr_surface* pSurface) {

exit:
    png_destroy_read_struct(&png_ptr, &info_ptr, NULL);

    if (fp != NULL) {
        fclose(fp);
    }
    if (result < 0) {
        if (surface) {
            free(surface);
        }
    }
    if (result < 0 && surface != NULL) free(surface);
    return result;
}

+1 −1
Original line number Diff line number Diff line
@@ -974,8 +974,8 @@ main(int argc, char **argv) {
    ui = device->GetUI();
    gCurrentUI = ui;

    ui->Init();
    ui->SetLocale(locale);
    ui->Init();
    ui->SetBackground(RecoveryUI::NONE);
    if (show_text) ui->ShowText(true);

+9 −5
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ static double now() {
ScreenRecoveryUI::ScreenRecoveryUI() :
    currentIcon(NONE),
    installingFrame(0),
    locale(NULL),
    rtl_locale(false),
    progressBarType(EMPTY),
    progressScopeStart(0),
@@ -306,21 +307,21 @@ void ScreenRecoveryUI::progress_loop() {
}

void ScreenRecoveryUI::LoadBitmap(const char* filename, gr_surface* surface) {
    int result = res_create_surface(filename, surface);
    int result = res_create_display_surface(filename, surface);
    if (result < 0) {
        LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
    }
}

void ScreenRecoveryUI::LoadBitmapArray(const char* filename, int* frames, gr_surface** surface) {
    int result = res_create_multi_surface(filename, frames, surface);
    int result = res_create_multi_display_surface(filename, frames, surface);
    if (result < 0) {
        LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
    }
}

void ScreenRecoveryUI::LoadLocalizedBitmap(const char* filename, gr_surface* surface) {
    int result = res_create_localized_surface(filename, surface);
    int result = res_create_localized_alpha_surface(filename, locale, surface);
    if (result < 0) {
        LOGE("missing bitmap %s\n(Code %d)\n", filename, result);
    }
@@ -360,8 +361,9 @@ void ScreenRecoveryUI::Init()
    RecoveryUI::Init();
}

void ScreenRecoveryUI::SetLocale(const char* locale) {
    if (locale) {
void ScreenRecoveryUI::SetLocale(const char* new_locale) {
    if (new_locale) {
        this->locale = new_locale;
        char* lang = strdup(locale);
        for (char* p = lang; *p; ++p) {
            if (*p == '_') {
@@ -380,6 +382,8 @@ void ScreenRecoveryUI::SetLocale(const char* locale) {
            rtl_locale = true;
        }
        free(lang);
    } else {
        new_locale = NULL;
    }
}

Loading