aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--android/multitouch-port.c11
-rw-r--r--android/multitouch-port.h6
-rw-r--r--android/multitouch-screen.c86
-rw-r--r--android/multitouch-screen.h34
-rw-r--r--android/utils/jpeg-compress.c20
-rw-r--r--android/utils/jpeg-compress.h8
-rw-r--r--distrib/jpeg-6b/jccolor.c53
-rw-r--r--distrib/jpeg-6b/jcparam.c1
-rw-r--r--vl-android.c7
9 files changed, 191 insertions, 35 deletions
diff --git a/android/multitouch-port.c b/android/multitouch-port.c
index c8e3ca5..9a9313c 100644
--- a/android/multitouch-port.c
+++ b/android/multitouch-port.c
@@ -409,10 +409,12 @@ static void
_fb_compress(const AndroidMTSPort* mtsp,
const MTFrameHeader* fmt,
const uint8_t* fb,
- int jpeg_quality)
+ int jpeg_quality,
+ int ydir)
{
jpeg_compressor_compress_fb(mtsp->jpeg_compressor, fmt->x, fmt->y, fmt->w,
- fmt->h, fmt->bpp, fmt->bpl, fb, jpeg_quality);
+ fmt->h, fmt->disp_height, fmt->bpp, fmt->bpl,
+ fb, jpeg_quality, ydir);
}
int
@@ -420,7 +422,8 @@ mts_port_send_frame(AndroidMTSPort* mtsp,
MTFrameHeader* fmt,
const uint8_t* fb,
async_send_cb cb,
- void* cb_opaque)
+ void* cb_opaque,
+ int ydir)
{
char* query;
int blob_size, off;
@@ -432,7 +435,7 @@ mts_port_send_frame(AndroidMTSPort* mtsp,
/* Compress framebuffer region. 10% quality seems to be sufficient. */
fmt->format = MTFB_JPEG;
- _fb_compress(mtsp, fmt, fb, 10);
+ _fb_compress(mtsp, fmt, fb, 10, ydir);
/* Total size of the blob: header + JPEG image. */
blob_size = sizeof(MTFrameHeader) +
diff --git a/android/multitouch-port.h b/android/multitouch-port.h
index 553617b..d99b3fd 100644
--- a/android/multitouch-port.h
+++ b/android/multitouch-port.h
@@ -120,6 +120,9 @@ extern int mts_port_stop(AndroidMTSPort* amtp);
* cb - Callback to invoke when update has been transferred to the MT-emulating
* application on the device.
* cb_opaque - An opaque parameter to pass back to the 'cb' callback.
+ * ydir - Indicates direction in which lines are arranged in the framebuffer. If
+ * this value is negative, lines are arranged in bottom-up format (i.e. the
+ * bottom line is at the beginning of the buffer).
* Return:
* 0 on success, or != 0 on failure.
*/
@@ -127,6 +130,7 @@ extern int mts_port_send_frame(AndroidMTSPort* mtsp,
MTFrameHeader* fmt,
const uint8_t* fb,
async_send_cb cb,
- void* cb_opaque);
+ void* cb_opaque,
+ int ydir);
#endif /* ANDROID_ANDROID_MULTITOUCH_PORT_H_ */
diff --git a/android/multitouch-screen.c b/android/multitouch-screen.c
index e1a9a8a..bd96f79 100644
--- a/android/multitouch-screen.c
+++ b/android/multitouch-screen.c
@@ -73,6 +73,12 @@ typedef struct MTSState {
/* Boolean value indicating if framebuffer updates are currently being
* transferred to the application running on the device. */
int fb_transfer_in_progress;
+ /* Indicates direction in which lines are arranged in the framebuffer. If
+ * this value is negative, lines are arranged in bottom-up format (i.e. the
+ * bottom line is at the beginning of the buffer). */
+ int ydir;
+ /* Current framebuffer pointer. */
+ uint8_t* current_fb;
} MTSState;
/* Default multi-touch screen descriptor */
@@ -228,6 +234,9 @@ _mts_pointer_move(MTSState* mts_state, int slot_index, int x, int y, int pressur
* Multi-touch API
*******************************************************************************/
+/* Multi-touch service initialization flag. */
+static int _is_mt_initialized = 0;
+
/* Callback that is invoked when framebuffer update has been transmitted to the
* device. */
static void
@@ -240,8 +249,8 @@ _on_fb_sent(void* opaque, ATResult res, void* data, int size, int sent)
if (mts_state->fb_header.w && mts_state->fb_header.h) {
/* Send accumulated updates. */
if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
- mts_state->ds->surface->data, _on_fb_sent,
- mts_state)) {
+ mts_state->current_fb, _on_fb_sent, mts_state,
+ mts_state->ydir)) {
mts_state->fb_transfer_in_progress = 0;
}
} else {
@@ -250,17 +259,12 @@ _on_fb_sent(void* opaque, ATResult res, void* data, int size, int sent)
}
}
-/* A callback invoked on framebuffer updates.
- * Param:
- * opaque - MTSState instance.
- * x, y, w, h - Defines an updated rectangle inside the framebuffer.
+/* Common handler for framebuffer updates invoked by both, software, and OpenGLES
+ * renderers.
*/
static void
-_mt_fb_update(void* opaque, int x, int y, int w, int h)
+_mt_fb_common_update(MTSState* mts_state, int x, int y, int w, int h)
{
- MTSState* const mts_state = (MTSState*)opaque;
- const DisplaySurface* const surface = mts_state->ds->surface;
-
if (mts_state->fb_header.w == 0 && mts_state->fb_header.h == 0) {
/* First update after previous one has been transmitted to the device. */
mts_state->fb_header.x = x;
@@ -297,31 +301,69 @@ _mt_fb_update(void* opaque, int x, int y, int w, int h)
mts_state->fb_header.h = bottom - mts_state->fb_header.y;
}
- /* TODO: Looks like general framebuffer properties can change on the fly.
- * Find a callback that can catch that. For now, just copy FB properties
- * over in every FB update. */
- mts_state->fb_header.bpp = surface->pf.bytes_per_pixel;
- mts_state->fb_header.bpl = surface->linesize;
- mts_state->fb_header.disp_width = surface->width;
- mts_state->fb_header.disp_height = surface->height;
-
/* We will send updates to the device only after previous transmission is
* completed. */
if (!mts_state->fb_transfer_in_progress) {
mts_state->fb_transfer_in_progress = 1;
if (mts_port_send_frame(mts_state->mtsp, &mts_state->fb_header,
- surface->data, _on_fb_sent, mts_state)) {
+ mts_state->current_fb, _on_fb_sent, mts_state,
+ mts_state->ydir)) {
mts_state->fb_transfer_in_progress = 0;
}
}
}
+/* A callback invoked on framebuffer updates by software renderer.
+ * Param:
+ * opaque - MTSState instance.
+ * x, y, w, h - Defines an updated rectangle inside the framebuffer.
+ */
+static void
+_mt_fb_update(void* opaque, int x, int y, int w, int h)
+{
+ MTSState* const mts_state = (MTSState*)opaque;
+ const DisplaySurface* const surface = mts_state->ds->surface;
+
+ /* TODO: For sofware renderer general framebuffer properties can change on
+ * the fly. Find a callback that can catch that. For now, just copy FB
+ * properties over in every FB update. */
+ mts_state->fb_header.bpp = surface->pf.bytes_per_pixel;
+ mts_state->fb_header.bpl = surface->linesize;
+ mts_state->fb_header.disp_width = surface->width;
+ mts_state->fb_header.disp_height = surface->height;
+ mts_state->current_fb = surface->data;
+ mts_state->ydir = 1;
+
+ _mt_fb_common_update(mts_state, x, y, w, h);
+}
void
-multitouch_init(AndroidMTSPort* mtsp)
+multitouch_opengles_fb_update(void* context,
+ int w, int h, int ydir,
+ int format, int type,
+ unsigned char* pixels)
{
- /* Multi-touch service initialization flag. */
- static int _is_mt_initialized = 0;
+ MTSState* const mts_state = &_MTSState;
+ /* Make sure MT port is initialized. */
+ if (!_is_mt_initialized) {
+ return;
+ }
+
+ /* GLES format is always RGBA8888 */
+ mts_state->fb_header.bpp = 4;
+ mts_state->fb_header.bpl = 4 * w;
+ mts_state->fb_header.disp_width = w;
+ mts_state->fb_header.disp_height = h;
+ mts_state->current_fb = pixels;
+ mts_state->ydir = ydir;
+
+ /* GLES emulator alwas update the entire framebuffer. */
+ _mt_fb_common_update(mts_state, 0, 0, w, h);
+}
+
+void
+multitouch_init(AndroidMTSPort* mtsp)
+{
if (!_is_mt_initialized) {
MTSState* const mts_state = &_MTSState;
DisplayState* const ds = get_displaystate();
diff --git a/android/multitouch-screen.h b/android/multitouch-screen.h
index 9288073..433944d 100644
--- a/android/multitouch-screen.h
+++ b/android/multitouch-screen.h
@@ -64,5 +64,37 @@ extern int multitouch_get_max_slot();
/* Saves screen size reported by the device that emulates multi-touch. */
extern void multitouch_set_device_screen_size(int width, int height);
-#endif /* ANDROID_MULTITOUCH_SCREEN_H_ */
+/* A callback set to monitor OpenGLES framebuffer updates.
+ * This callback is called by the renderer just before each new frame is
+ * displayed, providing a copy of the framebuffer contents.
+ * The callback will be called from one of the renderer's threads, so it may
+ * require synchronization on any data structures it modifies. The pixels buffer
+ * may be overwritten as soon as the callback returns.
+ * The pixels buffer is intentionally not const: the callback may modify the data
+ * without copying to another buffer if it wants, e.g. in-place RGBA to RGB
+ * conversion, or in-place y-inversion.
+ * Param:
+ * context The pointer optionally provided when the callback was
+ * registered. The client can use this to pass whatever
+ * information it wants to the callback.
+ * width, height Dimensions of the image, in pixels. Rows are tightly packed;
+ * there is no inter-row padding.
+ * ydir Indicates row order: 1 means top-to-bottom order, -1 means
+ * bottom-to-top order.
+ * format, type Format and type GL enums, as used in glTexImage2D() or
+ * glReadPixels(), describing the pixel format.
+ * pixels The framebuffer image.
+ *
+ * In the first implementation, ydir is always -1 (bottom to top), format and
+ * type are always GL_RGBA and GL_UNSIGNED_BYTE, and the width and height will
+ * always be the same as the ones passed to initOpenGLRenderer().
+ */
+extern void multitouch_opengles_fb_update(void* context,
+ int width,
+ int height,
+ int ydir,
+ int format,
+ int type,
+ unsigned char* pixels);
+#endif /* ANDROID_MULTITOUCH_SCREEN_H_ */
diff --git a/android/utils/jpeg-compress.c b/android/utils/jpeg-compress.c
index 2b42a00..cd45bf5 100644
--- a/android/utils/jpeg-compress.c
+++ b/android/utils/jpeg-compress.c
@@ -151,12 +151,14 @@ jpeg_compressor_get_header_size(const AJPEGDesc* dsc)
void
jpeg_compressor_compress_fb(AJPEGDesc* dsc,
- int x, int y, int w, int h,
+ int x, int y, int w, int h, int num_lines,
int bpp, int bpl,
const uint8_t* fb,
- int jpeg_quality){
+ int jpeg_quality,
+ int ydir){
struct jpeg_compress_struct cinfo = {0};
struct jpeg_error_mgr err_mgr;
+ const int x_shift = x * bpp;
/*
* Initialize compressin information structure, and start compression
@@ -186,9 +188,17 @@ jpeg_compressor_compress_fb(AJPEGDesc* dsc,
jpeg_start_compress(&cinfo, TRUE);
/* Line by line compress the region. */
- while (cinfo.next_scanline < cinfo.image_height) {
- JSAMPROW rgb = (JSAMPROW)(fb + (cinfo.next_scanline + y) * bpl + x * bpp);
- jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&rgb, 1);
+ if (ydir >= 0) {
+ while (cinfo.next_scanline < cinfo.image_height) {
+ JSAMPROW rgb = (JSAMPROW)(fb + (cinfo.next_scanline + y) * bpl + x_shift);
+ jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&rgb, 1);
+ }
+ } else {
+ const int y_shift = num_lines - y - 1;
+ while (cinfo.next_scanline < cinfo.image_height) {
+ JSAMPROW rgb = (JSAMPROW)(fb + (y_shift - cinfo.next_scanline) * bpl + x_shift);
+ jpeg_write_scanlines(&cinfo, (JSAMPARRAY)&rgb, 1);
+ }
}
/* Complete the compression. */
diff --git a/android/utils/jpeg-compress.h b/android/utils/jpeg-compress.h
index 4e0e61a..1f84a5d 100644
--- a/android/utils/jpeg-compress.h
+++ b/android/utils/jpeg-compress.h
@@ -77,17 +77,23 @@ extern int jpeg_compressor_get_header_size(const AJPEGDesc* dsc);
* Param:
* dsc - Compression descriptor, obtained with jpeg_compressor_create.
* x, y, w, h - Coordinates and sizes of framebuffer region to compress.
+ * num_lines - Number of lines in the framebuffer (true height).
* bpp - Number of bytes per pixel in the framebuffer.
* bpl - Number of bytes per line in the framebuffer.
* fb - Beginning of the framebuffer.
* jpeg_quality JPEG compression quality. A number from 1 to 100. Note that
* value 10 provides pretty decent image for the purpose of multi-touch
* emulation.
+ * ydir - Indicates direction in which lines are arranged in the framebuffer. If
+ * this value is negative, lines are arranged in bottom-up format (i.e. the
+ * bottom line is at the beginning of the buffer).
*/
extern void jpeg_compressor_compress_fb(AJPEGDesc* dsc,
int x, int y, int w, int h,
+ int num_lines,
int bpp, int bpl,
const uint8_t* fb,
- int jpeg_quality);
+ int jpeg_quality,
+ int ydir);
#endif /* _ANDROID_UTILS_JPEG_COMPRESS_H */
diff --git a/distrib/jpeg-6b/jccolor.c b/distrib/jpeg-6b/jccolor.c
index 67078ab..61e262d 100644
--- a/distrib/jpeg-6b/jccolor.c
+++ b/distrib/jpeg-6b/jccolor.c
@@ -219,6 +219,52 @@ rgb565_ycc_convert (j_compress_ptr cinfo,
}
}
}
+
+/* Converts RGBA8888 row into YCbCr */
+METHODDEF(void)
+rgba8888_ycc_convert (j_compress_ptr cinfo,
+ JSAMPARRAY input_buf, JSAMPIMAGE output_buf,
+ JDIMENSION output_row, int num_rows)
+{
+ my_cconvert_ptr cconvert = (my_cconvert_ptr) cinfo->cconvert;
+ register int r, g, b;
+ register INT32 * ctab = cconvert->rgb_ycc_tab;
+ register INT32* inptr;
+ register JSAMPROW outptr0, outptr1, outptr2;
+ register JDIMENSION col;
+ JDIMENSION num_cols = cinfo->image_width;
+
+ while (--num_rows >= 0) {
+ inptr = (INT32*)(*input_buf++);
+ outptr0 = output_buf[0][output_row];
+ outptr1 = output_buf[1][output_row];
+ outptr2 = output_buf[2][output_row];
+ output_row++;
+ for (col = 0; col < num_cols; col++) {
+ register const unsigned char* color = (unsigned char*)(inptr + col);
+ r = (*color) & 0xff; color++;
+ g = (*color) & 0xff; color++;
+ b = (*color) & 0xff; color++;
+ /* If the inputs are 0..MAXJSAMPLE, the outputs of these equations
+ * must be too; we do not need an explicit range-limiting operation.
+ * Hence the value being shifted is never negative, and we don't
+ * need the general RIGHT_SHIFT macro.
+ */
+ /* Y */
+ outptr0[col] = (JSAMPLE)
+ ((ctab[r+R_Y_OFF] + ctab[g+G_Y_OFF] + ctab[b+B_Y_OFF])
+ >> SCALEBITS);
+ /* Cb */
+ outptr1[col] = (JSAMPLE)
+ ((ctab[r+R_CB_OFF] + ctab[g+G_CB_OFF] + ctab[b+B_CB_OFF])
+ >> SCALEBITS);
+ /* Cr */
+ outptr2[col] = (JSAMPLE)
+ ((ctab[r+R_CR_OFF] + ctab[g+G_CR_OFF] + ctab[b+B_CR_OFF])
+ >> SCALEBITS);
+ }
+ }
+}
#endif /* ANDROID_RGB */
/**************** Cases other than RGB -> YCbCr **************/
@@ -505,6 +551,10 @@ jinit_color_converter (j_compress_ptr cinfo)
if (cinfo->input_components != 2)
ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
break;
+ case JCS_RGBA_8888:
+ if (cinfo->input_components != 4)
+ ERREXIT(cinfo, JERR_BAD_IN_COLORSPACE);
+ break;
#endif /* ANDROID_RGB */
default: /* JCS_UNKNOWN can be anything */
@@ -551,6 +601,9 @@ jinit_color_converter (j_compress_ptr cinfo)
else if (cinfo->in_color_space == JCS_RGB_565) {
cconvert->pub.start_pass = rgb_ycc_start;
cconvert->pub.color_convert = rgb565_ycc_convert;
+ } else if (cinfo->in_color_space == JCS_RGBA_8888) {
+ cconvert->pub.start_pass = rgb_ycc_start;
+ cconvert->pub.color_convert = rgba8888_ycc_convert;
}
#endif /* ANDROID_RGB */
else
diff --git a/distrib/jpeg-6b/jcparam.c b/distrib/jpeg-6b/jcparam.c
index b305179..f8404dd 100644
--- a/distrib/jpeg-6b/jcparam.c
+++ b/distrib/jpeg-6b/jcparam.c
@@ -379,6 +379,7 @@ jpeg_default_colorspace (j_compress_ptr cinfo)
break;
#ifdef ANDROID_RGB
case JCS_RGB_565:
+ case JCS_RGBA_8888:
jpeg_set_colorspace(cinfo, JCS_YCbCr);
break;
#endif /* ANDROID_RGB */
diff --git a/vl-android.c b/vl-android.c
index 67da637..5a41e2d 100644
--- a/vl-android.c
+++ b/vl-android.c
@@ -68,6 +68,7 @@
#include "android/utils/timezone.h"
#include "android/snapshot.h"
#include "android/opengles.h"
+#include "android/multitouch-screen.h"
#include "targphys.h"
#include "tcpdump.h"
@@ -3877,8 +3878,12 @@ int main(int argc, char **argv, char **envp)
if (android_hw->hw_gpu_enabled) {
if (android_initOpenglesEmulation() == 0) {
gles_emul = 1;
+ /* Set framebuffer change notification callback when starting
+ * GLES emulation. Currently only multi-touch emulation is
+ * interested in FB changes (to transmit them to the device), so
+ * the callback is set within MT emulation.*/
android_startOpenglesRenderer(android_hw->hw_lcd_width, android_hw->hw_lcd_height,
- NULL, NULL);
+ multitouch_opengles_fb_update, NULL);
} else {
dwarning("Could not initialize OpenglES emulation, using software renderer.");
}