You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

418 lines
11 KiB

  1. /*
  2. * This file is part of Aptdec.
  3. * Copyright (c) 2004-2009 Thierry Leconte (F4DWV), Xerbo (xerbo@protonmail.com) 2019-2022
  4. *
  5. * Aptdec is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 2 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <https://www.gnu.org/licenses/>.
  17. *
  18. */
  19. #include <png.h>
  20. #include <stdlib.h>
  21. #include <stdio.h>
  22. #include <string.h>
  23. #include <stdint.h>
  24. #include <math.h>
  25. #include "util.h"
  26. #include "pngio.h"
  27. int readRawImage(char *filename, float **prow, int *nrow) {
  28. FILE *fp = fopen(filename, "rb");
  29. printf("%s", filename);
  30. if(!fp) {
  31. error_noexit("Cannot open image");
  32. return 0;
  33. }
  34. // Create reader
  35. png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  36. if(!png) {
  37. fclose(fp);
  38. return 0;
  39. }
  40. png_infop info = png_create_info_struct(png);
  41. if(!info) {
  42. fclose(fp);
  43. return 0;
  44. }
  45. png_init_io(png, fp);
  46. // Read info from header
  47. png_read_info(png, info);
  48. int width = png_get_image_width(png, info);
  49. int height = png_get_image_height(png, info);
  50. png_byte color_type = png_get_color_type(png, info);
  51. png_byte bit_depth = png_get_bit_depth(png, info);
  52. // Check the image
  53. if(width != APT_IMG_WIDTH){
  54. error_noexit("Raw image must be 2080px wide");
  55. return 0;
  56. }else if(bit_depth != 8){
  57. error_noexit("Raw image must have 8 bit color");
  58. return 0;
  59. }else if(color_type != PNG_COLOR_TYPE_GRAY){
  60. error_noexit("Raw image must be grayscale");
  61. return 0;
  62. }
  63. // Create row buffers
  64. png_bytep *PNGrows = NULL;
  65. PNGrows = (png_bytep *) malloc(sizeof(png_bytep) * height);
  66. for(int y = 0; y < height; y++) PNGrows[y] = (png_byte *)
  67. malloc(png_get_rowbytes(png, info));
  68. // Read image
  69. png_read_image(png, PNGrows);
  70. // Tidy up
  71. fclose(fp);
  72. png_destroy_read_struct(&png, &info, NULL);
  73. // Put into prow
  74. *nrow = height;
  75. for(int y = 0; y < height; y++) {
  76. prow[y] = (float *) malloc(sizeof(float) * width);
  77. for(int x = 0; x < width; x++)
  78. prow[y][x] = (float)PNGrows[y][x];
  79. }
  80. return 1;
  81. }
  82. int readPalette(char *filename, apt_rgb_t **pixels) {
  83. FILE *fp = fopen(filename, "rb");
  84. if(!fp) {
  85. error_noexit("Cannot open palette");
  86. return 0;
  87. }
  88. // Create reader
  89. png_structp png = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  90. if(!png) {
  91. fclose(fp);
  92. return 0;
  93. }
  94. png_infop info = png_create_info_struct(png);
  95. if(!info) {
  96. fclose(fp);
  97. return 0;
  98. }
  99. png_init_io(png, fp);
  100. // Read info from header
  101. png_read_info(png, info);
  102. int width = png_get_image_width(png, info);
  103. int height = png_get_image_height(png, info);
  104. png_byte color_type = png_get_color_type(png, info);
  105. png_byte bit_depth = png_get_bit_depth(png, info);
  106. // Check the image
  107. if(width != 256 && height != 256){
  108. error_noexit("Palette must be 256x256");
  109. return 0;
  110. }else if(bit_depth != 8){
  111. error_noexit("Palette must be 8 bit color");
  112. return 0;
  113. }else if(color_type != PNG_COLOR_TYPE_RGB){
  114. error_noexit("Palette must be RGB");
  115. return 0;
  116. }
  117. // Create row buffers
  118. png_bytep *PNGrows = NULL;
  119. PNGrows = (png_bytep *) malloc(sizeof(png_bytep) * height);
  120. for(int y = 0; y < height; y++)
  121. PNGrows[y] = (png_byte *) malloc(png_get_rowbytes(png, info));
  122. // Read image
  123. png_read_image(png, PNGrows);
  124. // Tidy up
  125. fclose(fp);
  126. png_destroy_read_struct(&png, &info, NULL);
  127. // Put into crow
  128. for(int y = 0; y < height; y++) {
  129. pixels[y] = (apt_rgb_t *) malloc(sizeof(apt_rgb_t) * width);
  130. for(int x = 0; x < width; x++)
  131. pixels[y][x] = (apt_rgb_t){
  132. PNGrows[y][x*3],
  133. PNGrows[y][x*3 + 1],
  134. PNGrows[y][x*3 + 2]
  135. };
  136. }
  137. return 1;
  138. }
  139. void prow2crow(float **prow, int nrow, char *palette, apt_rgb_t **crow){
  140. for(int y = 0; y < nrow; y++){
  141. crow[y] = (apt_rgb_t *) malloc(sizeof(apt_rgb_t) * APT_IMG_WIDTH);
  142. for(int x = 0; x < APT_IMG_WIDTH; x++){
  143. if(palette == NULL)
  144. crow[y][x].r = crow[y][x].g = crow[y][x].b = prow[y][x];
  145. else
  146. crow[y][x] = apt_applyPalette(palette, prow[y][x]);
  147. }
  148. }
  149. }
  150. int applyUserPalette(float **prow, int nrow, char *filename, apt_rgb_t **crow){
  151. apt_rgb_t *pal_row[256];
  152. if(!readPalette(filename, pal_row)){
  153. error_noexit("Could not read palette");
  154. return 0;
  155. }
  156. for(int y = 0; y < nrow; y++){
  157. for(int x = 0; x < APT_CH_WIDTH; x++){
  158. int cha = CLIP(prow[y][x + APT_CHA_OFFSET], 0, 255);
  159. int chb = CLIP(prow[y][x + APT_CHB_OFFSET], 0, 255);
  160. crow[y][x + APT_CHA_OFFSET] = pal_row[chb][cha];
  161. }
  162. }
  163. return 1;
  164. }
  165. int ImageOut(options_t *opts, apt_image_t *img, int offset, int width, char *desc, char chid, char *palette){
  166. char outName[512];
  167. if(opts->filename == NULL || opts->filename[0] == '\0'){
  168. sprintf(outName, "%s/%s-%c.png", opts->path, img->name, chid);
  169. }else{
  170. sprintf(outName, "%s/%s", opts->path, opts->filename);
  171. }
  172. png_text meta[] = {
  173. {PNG_TEXT_COMPRESSION_NONE, "Software", VERSION, sizeof(VERSION)},
  174. {PNG_TEXT_COMPRESSION_NONE, "Channel", desc, sizeof(desc)},
  175. {PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}
  176. };
  177. // Parse image type
  178. int greyscale = 1;
  179. switch (chid){
  180. case Palleted:
  181. greyscale = 0;
  182. break;
  183. case Temperature:
  184. greyscale = 0;
  185. break;
  186. case Raw_Image: break;
  187. case Channel_A: break;
  188. case Channel_B: break;
  189. }
  190. // Parse effects
  191. int crop_telemetry = 0;
  192. for(unsigned long int i = 0; i < strlen(opts->effects); i++){
  193. switch (opts->effects[i]) {
  194. case Crop_Telemetry:
  195. width -= APT_TOTAL_TELE;
  196. offset += APT_SYNC_WIDTH + APT_SPC_WIDTH;
  197. crop_telemetry = 1;
  198. break;
  199. case Precipitation_Overlay:
  200. greyscale = 0;
  201. break;
  202. case Flip_Image: break;
  203. case Denoise: break;
  204. case Histogram_Equalise: break;
  205. case Linear_Equalise: break;
  206. case Crop_Noise: break;
  207. default: {
  208. char text[100];
  209. sprintf(text, "Unrecognised effect, \"%c\"", opts->effects[i]);
  210. warning(text);
  211. break;
  212. }
  213. }
  214. }
  215. FILE *pngfile;
  216. // Create writer
  217. png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  218. if (!png_ptr) {
  219. png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
  220. error_noexit("Could not create a PNG writer");
  221. return 0;
  222. }
  223. png_infop info_ptr = png_create_info_struct(png_ptr);
  224. if (!info_ptr) {
  225. png_destroy_write_struct(&png_ptr, (png_infopp) NULL);
  226. error_noexit("Could not create a PNG writer");
  227. return 0;
  228. }
  229. if(greyscale){
  230. // Greyscale image
  231. png_set_IHDR(png_ptr, info_ptr, width, img->nrow,
  232. 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
  233. PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  234. }else{
  235. // 8 bit RGB image
  236. png_set_IHDR(png_ptr, info_ptr, width, img->nrow,
  237. 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE,
  238. PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  239. }
  240. png_set_text(png_ptr, info_ptr, meta, 3);
  241. png_set_pHYs(png_ptr, info_ptr, 3636, 3636, PNG_RESOLUTION_METER);
  242. // Init I/O
  243. pngfile = fopen(outName, "wb");
  244. if (!pngfile) {
  245. error_noexit("Could not open PNG for writing");
  246. return 1;
  247. }
  248. png_init_io(png_ptr, pngfile);
  249. png_write_info(png_ptr, info_ptr);
  250. // Move prow into crow, crow ~ color rows, if required
  251. apt_rgb_t *crow[APT_MAX_HEIGHT];
  252. if(!greyscale){
  253. prow2crow(img->prow, img->nrow, palette, crow);
  254. }
  255. // Apply a user provided color palette
  256. if(chid == Palleted){
  257. applyUserPalette(img->prow, img->nrow, opts->palette, crow);
  258. }
  259. // Precipitation overlay
  260. if(CONTAINS(opts->effects, Precipitation_Overlay)){
  261. for(int y = 0; y < img->nrow; y++){
  262. for(int x = 0; x < APT_CH_WIDTH; x++){
  263. if(img->prow[y][x + APT_CHB_OFFSET] >= 198)
  264. crow[y][x + APT_CHB_OFFSET] = crow[y][x + APT_CHA_OFFSET] = apt_applyPalette(apt_PrecipPalette, img->prow[y][x + APT_CHB_OFFSET]-198);
  265. }
  266. }
  267. }
  268. printf("Writing %s", outName);
  269. // Float power macro (for gamma adjustment)
  270. #define POWF(a, b) (b == 1.0 ? a : exp(b * log(a)))
  271. float a = POWF(255, opts->gamma)/255;
  272. // Build image
  273. for (int y = 0; y < img->nrow; y++) {
  274. png_color pix[APT_IMG_WIDTH]; // Color
  275. png_byte mpix[APT_IMG_WIDTH]; // Mono
  276. int skip = 0;
  277. for (int x = 0; x < width; x++) {
  278. if(crop_telemetry && x == APT_CH_WIDTH)
  279. skip += APT_TELE_WIDTH + APT_SYNC_WIDTH + APT_SPC_WIDTH;
  280. if(greyscale){
  281. mpix[x] = POWF(img->prow[y][x + skip + offset], opts->gamma)/a;
  282. }else{
  283. pix[x] = (png_color){
  284. POWF(crow[y][x + skip + offset].r, opts->gamma)/a,
  285. POWF(crow[y][x + skip + offset].g, opts->gamma)/a,
  286. POWF(crow[y][x + skip + offset].b, opts->gamma)/a
  287. };
  288. }
  289. }
  290. if(greyscale){
  291. png_write_row(png_ptr, (png_bytep) mpix);
  292. }else{
  293. png_write_row(png_ptr, (png_bytep) pix);
  294. }
  295. }
  296. // Tidy up
  297. png_write_end(png_ptr, info_ptr);
  298. fclose(pngfile);
  299. printf("\nDone\n");
  300. png_destroy_write_struct(&png_ptr, &info_ptr);
  301. return 1;
  302. }
  303. // TODO: clean up everthing below this comment
  304. png_structp rt_png_ptr;
  305. png_infop rt_info_ptr;
  306. FILE *rt_pngfile;
  307. int initWriter(options_t *opts, apt_image_t *img, int width, int height, char *desc, char *chid){
  308. char outName[384];
  309. sprintf(outName, "%s/%s-%s.png", opts->path, img->name, chid);
  310. png_text meta[] = {
  311. {PNG_TEXT_COMPRESSION_NONE, "Software", VERSION, sizeof(VERSION)},
  312. {PNG_TEXT_COMPRESSION_NONE, "Channel", desc, sizeof(desc)},
  313. {PNG_TEXT_COMPRESSION_NONE, "Description", "NOAA satellite image", 20}
  314. };
  315. // Create writer
  316. rt_png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
  317. if (!rt_png_ptr) {
  318. png_destroy_write_struct(&rt_png_ptr, (png_infopp) NULL);
  319. error_noexit("Could not create a PNG writer");
  320. return 0;
  321. }
  322. rt_info_ptr = png_create_info_struct(rt_png_ptr);
  323. if (!rt_info_ptr) {
  324. png_destroy_write_struct(&rt_png_ptr, (png_infopp) NULL);
  325. error_noexit("Could not create a PNG writer");
  326. return 0;
  327. }
  328. // Greyscale image
  329. png_set_IHDR(rt_png_ptr, rt_info_ptr, width, height,
  330. 8, PNG_COLOR_TYPE_GRAY, PNG_INTERLACE_NONE,
  331. PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
  332. png_set_text(rt_png_ptr, rt_info_ptr, meta, 3);
  333. // Channel = 25cm wide
  334. png_set_pHYs(rt_png_ptr, rt_info_ptr, 3636, 3636, PNG_RESOLUTION_METER);
  335. // Init I/O
  336. rt_pngfile = fopen(outName, "wb");
  337. if (!rt_pngfile) {
  338. error_noexit("Could not open PNG for writing");
  339. return 0;
  340. }
  341. png_init_io(rt_png_ptr, rt_pngfile);
  342. png_write_info(rt_png_ptr, rt_info_ptr);
  343. // Turn off compression
  344. png_set_compression_level(rt_png_ptr, 0);
  345. return 1;
  346. }
  347. void pushRow(float *row, int width){
  348. png_byte pix[APT_IMG_WIDTH];
  349. for(int i = 0; i < width; i++)
  350. pix[i] = row[i];
  351. png_write_row(rt_png_ptr, (png_bytep) pix);
  352. }
  353. void closeWriter(){
  354. png_write_end(rt_png_ptr, rt_info_ptr);
  355. fclose(rt_pngfile);
  356. png_destroy_write_struct(&rt_png_ptr, &rt_info_ptr);
  357. }