1
0
mirror of https://codeberg.org/Freeyourgadget/Gadgetbridge synced 2024-12-29 03:55:49 +01:00

Move screenshot encoding to PebbleProtocol

This commit is contained in:
José Rebelo 2023-06-11 16:54:50 +01:00
parent 55cecceb38
commit 531aef61fd
4 changed files with 85 additions and 57 deletions

View File

@ -17,9 +17,13 @@
package nodomain.freeyourgadget.gadgetbridge.deviceevents; package nodomain.freeyourgadget.gadgetbridge.deviceevents;
public class GBDeviceEventScreenshot extends GBDeviceEvent { public class GBDeviceEventScreenshot extends GBDeviceEvent {
public int width; private final byte[] data;
public int height;
public byte bpp; public GBDeviceEventScreenshot(final byte[] data) {
public byte[] clut; this.data = data;
public byte[] data; }
public byte[] getData() {
return data;
}
} }

View File

@ -379,6 +379,11 @@ public abstract class AbstractDeviceSupport implements DeviceSupport {
} }
private void handleGBDeviceEvent(GBDeviceEventScreenshot screenshot) { private void handleGBDeviceEvent(GBDeviceEventScreenshot screenshot) {
if (screenshot.getData() == null) {
LOG.warn("Screnshot data is null");
return;
}
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-hhmmss", Locale.US); SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd-hhmmss", Locale.US);
String filename = "screenshot_" + dateFormat.format(new Date()) + ".bmp"; String filename = "screenshot_" + dateFormat.format(new Date()) + ".bmp";

View File

@ -27,6 +27,10 @@ import org.json.JSONObject;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
@ -65,6 +69,7 @@ import nodomain.freeyourgadget.gadgetbridge.model.NotificationType;
import nodomain.freeyourgadget.gadgetbridge.model.Weather; import nodomain.freeyourgadget.gadgetbridge.model.Weather;
import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec; import nodomain.freeyourgadget.gadgetbridge.model.WeatherSpec;
import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol; import nodomain.freeyourgadget.gadgetbridge.service.serial.GBDeviceProtocol;
import nodomain.freeyourgadget.gadgetbridge.util.FileUtils;
import nodomain.freeyourgadget.gadgetbridge.util.GB; import nodomain.freeyourgadget.gadgetbridge.util.GB;
public class PebbleProtocol extends GBDeviceProtocol { public class PebbleProtocol extends GBDeviceProtocol {
@ -281,7 +286,12 @@ public class PebbleProtocol extends GBDeviceProtocol {
boolean mEnablePebbleKit = false; boolean mEnablePebbleKit = false;
boolean mAlwaysACKPebbleKit = false; boolean mAlwaysACKPebbleKit = false;
private boolean mForceProtocol = false; private boolean mForceProtocol = false;
private GBDeviceEventScreenshot mDevEventScreenshot = null;
private byte[] screenshotData = null;
private int screenshotWidth;
private int screenshotHeight;
private byte screenshotBpp;
private byte[] screenshotClut;
private int mScreenshotRemaining = -1; private int mScreenshotRemaining = -1;
//monochrome black + white //monochrome black + white
@ -1945,27 +1955,26 @@ public class PebbleProtocol extends GBDeviceProtocol {
} }
private GBDeviceEventScreenshot decodeScreenshot(ByteBuffer buf, int length) { private GBDeviceEventScreenshot decodeScreenshot(ByteBuffer buf, int length) {
if (mDevEventScreenshot == null) { if (screenshotData == null) {
byte result = buf.get(); byte result = buf.get();
mDevEventScreenshot = new GBDeviceEventScreenshot();
int version = buf.getInt(); int version = buf.getInt();
if (result != 0) { if (result != 0) {
return null; return null;
} }
mDevEventScreenshot.width = buf.getInt(); screenshotWidth = buf.getInt();
mDevEventScreenshot.height = buf.getInt(); screenshotHeight = buf.getInt();
if (version == 1) { if (version == 1) {
mDevEventScreenshot.bpp = 1; screenshotBpp = 1;
mDevEventScreenshot.clut = clut_pebble; screenshotClut = clut_pebble;
} else { } else {
mDevEventScreenshot.bpp = 8; screenshotBpp = 8;
mDevEventScreenshot.clut = clut_pebbletime; screenshotClut = clut_pebbletime;
} }
mScreenshotRemaining = (mDevEventScreenshot.width * mDevEventScreenshot.height * mDevEventScreenshot.bpp) / 8; mScreenshotRemaining = (screenshotWidth * screenshotHeight * screenshotBpp) / 8;
mDevEventScreenshot.data = new byte[mScreenshotRemaining]; screenshotData = new byte[mScreenshotRemaining];
length -= 13; length -= 13;
} }
if (mScreenshotRemaining == -1) { if (mScreenshotRemaining == -1) {
@ -1973,26 +1982,68 @@ public class PebbleProtocol extends GBDeviceProtocol {
} }
for (int i = 0; i < length; i++) { for (int i = 0; i < length; i++) {
byte corrected = buf.get(); byte corrected = buf.get();
if (mDevEventScreenshot.bpp == 1) { if (screenshotBpp == 1) {
corrected = reverseBits(corrected); corrected = reverseBits(corrected);
} else { } else {
corrected = (byte) (corrected & 0b00111111); corrected = (byte) (corrected & 0b00111111);
} }
mDevEventScreenshot.data[mDevEventScreenshot.data.length - mScreenshotRemaining + i] = corrected; screenshotData[screenshotData.length - mScreenshotRemaining + i] = corrected;
} }
mScreenshotRemaining -= length; mScreenshotRemaining -= length;
LOG.info("Screenshot remaining bytes " + mScreenshotRemaining); LOG.info("Screenshot remaining bytes " + mScreenshotRemaining);
if (mScreenshotRemaining == 0) { if (mScreenshotRemaining == 0) {
mScreenshotRemaining = -1; mScreenshotRemaining = -1;
LOG.info("Got screenshot : " + mDevEventScreenshot.width + "x" + mDevEventScreenshot.height + " " + "pixels"); LOG.info("Got screenshot : " + screenshotWidth + "x" + screenshotHeight + " " + "pixels");
GBDeviceEventScreenshot devEventScreenshot = mDevEventScreenshot; GBDeviceEventScreenshot devEventScreenshot = new GBDeviceEventScreenshot(encodeScreenshotBmp());
mDevEventScreenshot = null; screenshotData = null;
return devEventScreenshot; return devEventScreenshot;
} }
return null; return null;
} }
private byte[] encodeScreenshotBmp() {
final int FILE_HEADER_SIZE = 14;
final int INFO_HEADER_SIZE = 40;
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
ByteBuffer headerbuf = ByteBuffer.allocate(FILE_HEADER_SIZE + INFO_HEADER_SIZE + screenshotClut.length);
headerbuf.order(ByteOrder.LITTLE_ENDIAN);
// file header
headerbuf.put((byte) 'B');
headerbuf.put((byte) 'M');
headerbuf.putInt(0); // size in bytes (uncompressed = 0)
headerbuf.putInt(0); // reserved
headerbuf.putInt(FILE_HEADER_SIZE + INFO_HEADER_SIZE + screenshotClut.length);
// info header
headerbuf.putInt(INFO_HEADER_SIZE);
headerbuf.putInt(screenshotWidth);
headerbuf.putInt(-screenshotHeight);
headerbuf.putShort((short) 1); // planes
headerbuf.putShort((short) screenshotBpp);
headerbuf.putInt(0); // compression
headerbuf.putInt(0); // length of pixeldata in bytes (uncompressed=0)
headerbuf.putInt(0); // pixels per meter (x)
headerbuf.putInt(0); // pixels per meter (y)
headerbuf.putInt(screenshotClut.length / 4); // number of colors in CLUT
headerbuf.putInt(0); // numbers of used colors
headerbuf.put(screenshotClut);
baos.write(headerbuf.array());
int rowbytes = (screenshotWidth * screenshotBpp) / 8;
byte[] pad = new byte[rowbytes % 4];
for (int i = 0; i < screenshotHeight; i++) {
baos.write(screenshotData, rowbytes * i, rowbytes);
baos.write(pad);
}
return baos.toByteArray();
} catch (final IOException e) {
LOG.warn("Failed to encode screenshot to bpm");
return null;
}
}
private GBDeviceEvent[] decodeAction(ByteBuffer buf) { private GBDeviceEvent[] decodeAction(ByteBuffer buf) {
buf.order(ByteOrder.LITTLE_ENDIAN); buf.order(ByteOrder.LITTLE_ENDIAN);
byte command = buf.get(); byte command = buf.get();

View File

@ -347,44 +347,12 @@ public class GB {
} }
public static String writeScreenshot(GBDeviceEventScreenshot screenshot, String filename) throws IOException { public static String writeScreenshot(GBDeviceEventScreenshot screenshot, String filename) throws IOException {
LOG.info("Will write screenshot as {}", filename);
LOG.info("Will write screenshot: " + screenshot.width + "x" + screenshot.height + "x" + screenshot.bpp + "bpp"); final File dir = FileUtils.getExternalFilesDir();
final int FILE_HEADER_SIZE = 14; final File outputFile = new File(dir, filename);
final int INFO_HEADER_SIZE = 40;
File dir = FileUtils.getExternalFilesDir();
File outputFile = new File(dir, filename);
try (FileOutputStream fos = new FileOutputStream(outputFile)) { try (FileOutputStream fos = new FileOutputStream(outputFile)) {
ByteBuffer headerbuf = ByteBuffer.allocate(FILE_HEADER_SIZE + INFO_HEADER_SIZE + screenshot.clut.length); fos.write(screenshot.getData());
headerbuf.order(ByteOrder.LITTLE_ENDIAN);
// file header
headerbuf.put((byte) 'B');
headerbuf.put((byte) 'M');
headerbuf.putInt(0); // size in bytes (uncompressed = 0)
headerbuf.putInt(0); // reserved
headerbuf.putInt(FILE_HEADER_SIZE + INFO_HEADER_SIZE + screenshot.clut.length);
// info header
headerbuf.putInt(INFO_HEADER_SIZE);
headerbuf.putInt(screenshot.width);
headerbuf.putInt(-screenshot.height);
headerbuf.putShort((short) 1); // planes
headerbuf.putShort((short) screenshot.bpp);
headerbuf.putInt(0); // compression
headerbuf.putInt(0); // length of pixeldata in bytes (uncompressed=0)
headerbuf.putInt(0); // pixels per meter (x)
headerbuf.putInt(0); // pixels per meter (y)
headerbuf.putInt(screenshot.clut.length / 4); // number of colors in CLUT
headerbuf.putInt(0); // numbers of used colors
headerbuf.put(screenshot.clut);
fos.write(headerbuf.array());
int rowbytes = (screenshot.width * screenshot.bpp) / 8;
byte[] pad = new byte[rowbytes % 4];
for (int i = 0; i < screenshot.height; i++) {
fos.write(screenshot.data, rowbytes * i, rowbytes);
fos.write(pad);
}
} }
return outputFile.getAbsolutePath(); return outputFile.getAbsolutePath();
} }