You've already forked android_translation_layer
mirror of
https://gitlab.com/android_translation_layer/android_translation_layer.git
synced 2025-10-27 11:48:10 -07:00
NinePatchPaintable: new GdkPaintable class to render .9.png files
This commit is contained in:
@@ -83,6 +83,7 @@ libtranslationlayer_so = shared_library('translation_layer_main', [
|
||||
'src/api-impl-jni/graphics/android_graphics_drawable_Drawable.c',
|
||||
'src/api-impl-jni/graphics/android_graphics_drawable_DrawableContainer.c',
|
||||
'src/api-impl-jni/graphics/android_graphics_Typeface.c',
|
||||
'src/api-impl-jni/graphics/NinePatchPaintable.c',
|
||||
'src/api-impl-jni/media/android_media_MediaCodec.c',
|
||||
'src/api-impl-jni/android_content_res_AssetManager.c',
|
||||
'src/api-impl-jni/audio/android_media_AudioTrack.c',
|
||||
|
||||
139
src/api-impl-jni/graphics/NinePatchPaintable.c
Normal file
139
src/api-impl-jni/graphics/NinePatchPaintable.c
Normal file
@@ -0,0 +1,139 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "NinePatchPaintable.h"
|
||||
|
||||
enum {
|
||||
// The 9 patch segment is not a solid color.
|
||||
NO_COLOR = 0x00000001,
|
||||
// The 9 patch segment is completely transparent.
|
||||
TRANSPARENT_COLOR = 0x00000000
|
||||
};
|
||||
|
||||
static void ninepatch_paintable_snapshot(GdkPaintable *paintable, GdkSnapshot *snapshot, double width, double height)
|
||||
{
|
||||
int i, j;
|
||||
NinePatchPaintable *ninepatch = NINEPATCH_PAINTABLE(paintable);
|
||||
struct Res_png_9patch *chunk = ninepatch->chunk;
|
||||
if (chunk) {
|
||||
int32_t *xDivs = (void *)chunk + chunk->xDivsOffset;
|
||||
int32_t *yDivs = (void *)chunk + chunk->yDivsOffset;
|
||||
int32_t *color = (void *)chunk + chunk->colorsOffset;
|
||||
float strech_factor_width = (width - (ninepatch->width - ninepatch->strechy_width)) / ninepatch->strechy_width;
|
||||
float strech_factor_height = (height - (ninepatch->height - ninepatch->strechy_height)) / ninepatch->strechy_height;
|
||||
|
||||
graphene_rect_t rect;
|
||||
GdkRGBA rgba;
|
||||
for (j = 0, rect.origin.y = 0; j < chunk->numYDivs+1; j++, rect.origin.y += rect.size.height) {
|
||||
int div_start = j ? yDivs[j-1] : 0;
|
||||
int div_end = (j == chunk->numYDivs) ? ninepatch->height : yDivs[j];
|
||||
float patch_height = div_end - div_start;
|
||||
if (j%2) // odd sections are stretchable
|
||||
patch_height *= strech_factor_height;
|
||||
rect.size.height = patch_height;
|
||||
if (!patch_height) // skip empty sections
|
||||
continue;
|
||||
for (i = 0, rect.origin.x = 0; i < chunk->numXDivs+1; i++, rect.origin.x += rect.size.width) {
|
||||
int div_start = i ? xDivs[i-1] : 0;
|
||||
int div_end = (i == chunk->numXDivs) ? ninepatch->width : xDivs[i];
|
||||
float patch_width = div_end - div_start;
|
||||
if (i%2) // odd sections are stretchable
|
||||
patch_width *= strech_factor_width;
|
||||
rect.size.width = patch_width;
|
||||
if (!patch_width) // skip empty sections
|
||||
continue;
|
||||
if (*color == NO_COLOR) {
|
||||
printf("NinePatchPaintable: warning NO_COLOR sections not yet implemented");
|
||||
} else if (*color != TRANSPARENT_COLOR) {
|
||||
rgba.red = (*color >> 24 & 0xFF) / 255.f;
|
||||
rgba.green = (*color >> 16 & 0xFF) / 255.f;
|
||||
rgba.blue = (*color >> 8 & 0xFF) / 255.f;
|
||||
rgba.alpha = (*color & 0xFF) / 255.f;
|
||||
gtk_snapshot_append_color(snapshot, &rgba, &rect);
|
||||
}
|
||||
color++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static GdkPaintableFlags ninepatch_paintable_get_flags(GdkPaintable *paintable)
|
||||
{
|
||||
return GDK_PAINTABLE_STATIC_CONTENTS | GDK_PAINTABLE_STATIC_SIZE;
|
||||
}
|
||||
|
||||
static void ninepatch_paintable_init(NinePatchPaintable *ninepatch_paintable)
|
||||
{
|
||||
}
|
||||
|
||||
static void ninepatch_paintable_paintable_init(GdkPaintableInterface *iface)
|
||||
{
|
||||
iface->snapshot = ninepatch_paintable_snapshot;
|
||||
iface->get_flags = ninepatch_paintable_get_flags;
|
||||
}
|
||||
|
||||
static void ninepatch_paintable_class_init(NinePatchPaintableClass *class)
|
||||
{
|
||||
}
|
||||
|
||||
G_DEFINE_TYPE_WITH_CODE(NinePatchPaintable, ninepatch_paintable, G_TYPE_OBJECT,
|
||||
G_IMPLEMENT_INTERFACE(GDK_TYPE_PAINTABLE, ninepatch_paintable_paintable_init))
|
||||
|
||||
GdkPaintable *ninepatch_paintable_new(const char *path)
|
||||
{
|
||||
struct Res_png_9patch *chunk = NULL;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
int strechy_width = 0;
|
||||
int strechy_height = 0;
|
||||
uint8_t buf[8];
|
||||
int i;
|
||||
FILE *f = fopen(path, "r");
|
||||
fread(buf, 1, 8, f);
|
||||
if (buf[0] != 0x89 || buf[1] != 'P' || buf[2] != 'N' || buf[3] != 'G') {
|
||||
return NULL;
|
||||
}
|
||||
while (!feof(f)) {
|
||||
fread(buf, 1, 8, f);
|
||||
int size = ntohl(((uint32_t *)buf)[0]);
|
||||
size += 4; // 4 bytes checksum
|
||||
|
||||
if (!strncmp((char *)&buf[4], "npTc", 4)) {
|
||||
chunk = malloc(size);
|
||||
fread(chunk, 1, size, f);
|
||||
break;
|
||||
} else {
|
||||
fseek(f, size, SEEK_CUR);
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
|
||||
if (!chunk)
|
||||
return NULL;
|
||||
|
||||
int32_t *xDivs = (void *)chunk + chunk->xDivsOffset;
|
||||
int32_t *yDivs = (void *)chunk + chunk->yDivsOffset;
|
||||
|
||||
for (i = 0; i < chunk->numXDivs; i++)
|
||||
xDivs[i] = ntohl(xDivs[i]);
|
||||
for (i = 0; i < chunk->numYDivs; i++)
|
||||
yDivs[i] = ntohl(yDivs[i]);
|
||||
|
||||
GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file(path, NULL);
|
||||
width = gdk_pixbuf_get_width(pixbuf);
|
||||
height = gdk_pixbuf_get_height(pixbuf);
|
||||
g_object_unref(pixbuf);
|
||||
|
||||
for (i = 1; i < chunk->numXDivs+1; i+=2)
|
||||
strechy_width += (i == chunk->numXDivs ? width : xDivs[i]) - xDivs[i-1];
|
||||
for (i = 1; i < chunk->numYDivs+1; i+=2)
|
||||
strechy_height += (i == chunk->numYDivs ? height : yDivs[i]) - yDivs[i-1];
|
||||
|
||||
NinePatchPaintable *ninepatch = NINEPATCH_PAINTABLE(g_object_new(ninepatch_paintable_get_type(), NULL));
|
||||
ninepatch->chunk = chunk;
|
||||
ninepatch->width = width;
|
||||
ninepatch->height = height;
|
||||
ninepatch->strechy_width = strechy_width;
|
||||
ninepatch->strechy_height = strechy_height;
|
||||
return GDK_PAINTABLE(ninepatch);
|
||||
}
|
||||
33
src/api-impl-jni/graphics/NinePatchPaintable.h
Normal file
33
src/api-impl-jni/graphics/NinePatchPaintable.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#include <gdk/gdk.h>
|
||||
|
||||
struct Res_png_9patch
|
||||
{
|
||||
int8_t wasDeserialized;
|
||||
uint8_t numXDivs;
|
||||
uint8_t numYDivs;
|
||||
uint8_t numColors;
|
||||
// The offset (from the start of this structure) to the xDivs & yDivs
|
||||
// array for this 9patch. To get a pointer to this array, call
|
||||
// getXDivs or getYDivs. Note that the serialized form for 9patches places
|
||||
// the xDivs, yDivs and colors arrays immediately after the location
|
||||
// of the Res_png_9patch struct.
|
||||
uint32_t xDivsOffset;
|
||||
uint32_t yDivsOffset;
|
||||
int32_t paddingLeft, paddingRight;
|
||||
int32_t paddingTop, paddingBottom;
|
||||
// The offset (from the start of this structure) to the colors array
|
||||
// for this 9patch.
|
||||
uint32_t colorsOffset;
|
||||
} __attribute__((packed));
|
||||
|
||||
struct _NinePatchPaintable {
|
||||
GObject parent_instance;
|
||||
struct Res_png_9patch *chunk;
|
||||
int width;
|
||||
int height;
|
||||
int strechy_width;
|
||||
int strechy_height;
|
||||
};
|
||||
G_DECLARE_FINAL_TYPE(NinePatchPaintable, ninepatch_paintable, NINEPATCH, PAINTABLE, GObject)
|
||||
|
||||
GdkPaintable *ninepatch_paintable_new(const char *path);
|
||||
@@ -1,12 +1,21 @@
|
||||
#include <gdk/gdk.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../defines.h"
|
||||
#include "NinePatchPaintable.h"
|
||||
#include "../generated_headers/android_graphics_drawable_Drawable.h"
|
||||
|
||||
JNIEXPORT jlong JNICALL Java_android_graphics_drawable_Drawable_native_1paintable_1from_1path(JNIEnv *env, jclass class, jstring pathStr) {
|
||||
const char *path = (*env)->GetStringUTFChars(env, pathStr, NULL);
|
||||
GdkPaintable *paintable = NULL;
|
||||
|
||||
GdkPaintable *paintable = GDK_PAINTABLE(gdk_texture_new_from_filename(path, NULL));
|
||||
// check if path ends with .9.png
|
||||
int len = strlen(path);
|
||||
if (len >= 6 && !strcmp(path + len - 6, ".9.png")) {
|
||||
paintable = ninepatch_paintable_new(path);
|
||||
}
|
||||
if (!paintable)
|
||||
paintable = GDK_PAINTABLE(gdk_texture_new_from_filename(path, NULL));
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, pathStr, path);
|
||||
return _INTPTR(paintable);
|
||||
|
||||
Reference in New Issue
Block a user