CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/Data/Format/ZIMSave.cpp
Views: 1401
#include <cstdio>1#include <cstring>2#include <cmath>3#include <zstd.h>45#include "zlib.h"67#include "Common/Log.h"8#include "Common/Data/Format/ZIMLoad.h"9#include "Common/Data/Format/ZIMSave.h"1011static const char magic[5] = "ZIMG";1213/*int num_levels = 1;14if (flags & ZIM_HAS_MIPS) {15num_levels = log2i(width > height ? width : height);16}*/17static unsigned int log2i(unsigned int val) {18unsigned int ret = -1;19while (val != 0) {20val >>= 1; ret++;21}22return ret;23}242526int ezcompress(unsigned char* pDest, long* pnDestLen, const unsigned char* pSrc, long nSrcLen, int compressLevel) {27z_stream stream;28int err;2930int nExtraChunks;31uInt destlen;3233stream.next_in = (Bytef*)pSrc;34stream.avail_in = (uInt)nSrcLen;35#ifdef MAXSEG_64K36/* Check for source > 64K on 16-bit machine: */37if ((uLong)stream.avail_in != nSrcLen) return Z_BUF_ERROR;38#endif39destlen = (uInt)*pnDestLen;40if ((uLong)destlen != (uLong)*pnDestLen) return Z_BUF_ERROR;41stream.zalloc = (alloc_func)0;42stream.zfree = (free_func)0;43stream.opaque = (voidpf)0;4445err = deflateInit(&stream, compressLevel);46if (err != Z_OK) return err;47nExtraChunks = 0;48do {49stream.next_out = pDest;50stream.avail_out = destlen;51err = deflate(&stream, Z_FINISH);52if (err == Z_STREAM_END )53break;54if (err != Z_OK) {55deflateEnd(&stream);56return err;57}58nExtraChunks += 1;59} while (stream.avail_out == 0);6061*pnDestLen = stream.total_out;6263err = deflateEnd(&stream);64if (err != Z_OK) return err;6566return nExtraChunks ? Z_BUF_ERROR : Z_OK;67}6869inline int clamp16(int x) { if (x < 0) return 0; if (x > 15) return 15; return x; }70inline int clamp32(int x) { if (x < 0) return 0; if (x > 31) return 31; return x; }71inline int clamp64(int x) { if (x < 0) return 0; if (x > 63) return 63; return x; }727374bool ispowerof2 (int x) {75if (!x || (x&(x-1)))76return false;77else78return true;79}8081void Convert(const uint8_t *image_data, int width, int height, int pitch, int flags,82uint8_t **data, int *data_size) {83// For 4444 and 565. Ordered dither matrix. looks really surprisingly good on cell phone screens at 4444.84int dith[16] = {851, 9, 3, 11,8613, 5, 15, 7,874, 12, 2, 10,8816, 8, 14, 689};90if ((flags & ZIM_DITHER) == 0) {91for (int i = 0; i < 16; i++) { dith[i] = 8; }92}93switch (flags & ZIM_FORMAT_MASK) {94case ZIM_RGBA8888:95{96*data_size = width * height * 4;97*data = new uint8_t[width * height * 4];98for (int y = 0; y < height; y++) {99memcpy((*data) + y * width * 4, image_data + y * pitch, width * 4);100}101break;102}103case ZIM_RGBA4444:104{105*data_size = width * height * 2;106*data = new uint8_t[*data_size];107uint16_t *dst = (uint16_t *)(*data);108int i = 0;109for (int y = 0; y < height; y++) {110for (int x = 0; x < width; x++) {111int dithval = dith[(x&3)+((y&0x3)<<2)] - 8;112int r = clamp16((image_data[i * 4] + dithval) >> 4);113int g = clamp16((image_data[i * 4 + 1] + dithval) >> 4);114int b = clamp16((image_data[i * 4 + 2] + dithval) >> 4);115int a = clamp16((image_data[i * 4 + 3] + dithval) >> 4); // really dither alpha?116// Note: GL_UNSIGNED_SHORT_4_4_4_4, not GL_UNSIGNED_SHORT_4_4_4_4_REV117*dst++ = (r << 12) | (g << 8) | (b << 4) | (a << 0);118i++;119}120}121break;122}123case ZIM_RGB565:124{125*data_size = width * height * 2;126*data = new uint8_t[*data_size];127uint16_t *dst = (uint16_t *)(*data);128int i = 0;129for (int y = 0; y < height; y++) {130for (int x = 0; x < width; x++) {131int dithval = dith[(x&3)+((y&0x3)<<2)] - 8;132//dithval = 0; please check this133int r = clamp32((image_data[i * 4] + dithval/2) >> 3);134int g = clamp64((image_data[i * 4 + 1] + dithval/4) >> 2);135int b = clamp32((image_data[i * 4 + 2] + dithval/2) >> 3);136// Note: GL_UNSIGNED_SHORT_5_6_5, not GL_UNSIGNED_SHORT_5_6_5_REV137*dst++ = (r << 11) | (g << 5) | (b);138i++;139}140}141}142break;143144default:145ERROR_LOG(Log::IO, "Unhandled ZIM format %d", flags & ZIM_FORMAT_MASK);146*data = 0;147*data_size = 0;148return;149}150}151152// Deletes the old buffer.153uint8_t *DownsampleBy2(const uint8_t *image, int width, int height, int pitch) {154uint8_t *out = new uint8_t[(width/2) * (height/2) * 4];155156int degamma[256];157int gamma[32768];158for (int i =0; i < 256; i++) {159degamma[i] = (int)(powf((float)i / 255.0f, 1.0f/2.2f) * 8191.0f);160}161for (int i = 0; i < 32768; i++) {162gamma[i] = (int)(powf((float)i / 32764.0f, 2.2f) * 255.0f);163}164165// Really stupid mipmap downsampling - at least it does gamma though.166for (int y = 0; y < height; y+=2) {167for (int x = 0; x < width; x+=2) {168const uint8_t *tl = image + pitch * y + x*4;169const uint8_t *tr = tl + 4;170const uint8_t *bl = tl + pitch;171const uint8_t *br = bl + 4;172uint8_t *d = out + ((y/2) * ((width/2)) + x / 2) * 4;173for (int c = 0; c < 4; c++) {174d[c] = gamma[degamma[tl[c]] + degamma[tr[c]] + degamma[bl[c]] + degamma[br[c]]];175}176}177}178return out;179}180181void SaveZIM(FILE *f, int width, int height, int pitch, int flags, const uint8_t *image_data, int compressLevel) {182fwrite(magic, 1, 4, f);183fwrite(&width, 1, 4, f);184fwrite(&height, 1, 4, f);185fwrite(&flags, 1, 4, f);186187int num_levels = 1;188if (flags & ZIM_HAS_MIPS) {189num_levels = log2i(width > height ? height : width) + 1;190}191for (int i = 0; i < num_levels; i++) {192uint8_t *data = 0;193int data_size;194Convert(image_data, width, height, pitch, flags, &data, &data_size);195if (flags & ZIM_ZLIB_COMPRESSED) {196long dest_len = data_size * 2;197uint8_t *dest = new uint8_t[dest_len];198if (Z_OK == ezcompress(dest, &dest_len, data, data_size, compressLevel == 0 ? Z_DEFAULT_COMPRESSION : compressLevel)) {199fwrite(dest, 1, dest_len, f);200} else {201ERROR_LOG(Log::IO, "Zlib compression failed.\n");202}203delete [] dest;204} else if (flags & ZIM_ZSTD_COMPRESSED) {205size_t dest_len = ZSTD_compressBound(data_size);206uint8_t *dest = new uint8_t[dest_len];207dest_len = ZSTD_compress(dest, dest_len, data, data_size, compressLevel == 0 ? 22 : compressLevel);208if (!ZSTD_isError(dest_len)) {209fwrite(dest, 1, dest_len, f);210} else {211ERROR_LOG(Log::IO, "Zlib compression failed.\n");212}213delete [] dest;214} else {215fwrite(data, 1, data_size, f);216}217delete [] data;218219if (i != num_levels - 1) {220uint8_t *smaller = DownsampleBy2(image_data, width, height, pitch);221if (i != 0) {222delete [] image_data;223}224image_data = smaller;225width /= 2;226height /= 2;227pitch = width * 4;228}229}230delete [] image_data;231}232233234